From: VMware, Inc <> Date: Mon, 26 Jul 2010 19:13:53 +0000 (-0700) Subject: Remove vmware-user from open-vm-tools. X-Git-Tag: 2010.07.25-280253~21 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=55b6931ac5c82d426eb8e87cd366bab3a4f71e35;p=thirdparty%2Fopen-vm-tools.git Remove vmware-user from open-vm-tools. This change does a little bit more than just remove the sources. It also modifies the vmware-user-suid-wrapper so that it executes "vmtoolsd -n vmusr" in open-vm-tools. The desktop file is also fixed to execute the suid wrapper, which is the right thing to do. Also remove locationsdb.c from open-vm-tools, since it's not used there. Signed-off-by: Marcelo Vanzin --- diff --git a/open-vm-tools/Makefile.am b/open-vm-tools/Makefile.am index 2389e786e..b0cf55762 100644 --- a/open-vm-tools/Makefile.am +++ b/open-vm-tools/Makefile.am @@ -22,18 +22,10 @@ # that all of our macros are in the 'm4' subdirectory. ACLOCAL_AMFLAGS = -I m4 -# These subdirectories require X, and may be disabled using --without-x -X11_SUBDIRS = -X11_SUBDIRS += vmware-user -X11_SUBDIRS += vmware-user-suid-wrapper - SUBDIRS = SUBDIRS += lib SUBDIRS += libvmtools SUBDIRS += libhgfs -if HAVE_X11 - SUBDIRS += $(X11_SUBDIRS) -endif SUBDIRS += hgfsclient if BUILD_HGFSMOUNTER SUBDIRS += hgfsmounter @@ -44,6 +36,9 @@ SUBDIRS += rpctool SUBDIRS += scripts SUBDIRS += services SUBDIRS += toolbox +if HAVE_X11 + SUBDIRS += vmware-user-suid-wrapper +endif if HAVE_FUSE SUBDIRS += vmblock-fuse endif diff --git a/open-vm-tools/configure.ac b/open-vm-tools/configure.ac index 3f9bc1fb4..d7dd098bb 100644 --- a/open-vm-tools/configure.ac +++ b/open-vm-tools/configure.ac @@ -1278,7 +1278,6 @@ AC_CONFIG_FILES([ \ services/plugins/vix/Makefile \ services/plugins/vixUser/Makefile \ services/plugins/vmbackup/Makefile \ - vmware-user/Makefile \ vmware-user-suid-wrapper/Makefile \ toolbox/Makefile \ hgfsclient/Makefile \ diff --git a/open-vm-tools/vmware-user-suid-wrapper/Makefile.am b/open-vm-tools/vmware-user-suid-wrapper/Makefile.am index 2a0c5fec2..346ffe100 100644 --- a/open-vm-tools/vmware-user-suid-wrapper/Makefile.am +++ b/open-vm-tools/vmware-user-suid-wrapper/Makefile.am @@ -17,12 +17,8 @@ bin_PROGRAMS = vmware-user-suid-wrapper -# We shouldn't impose on users' installation preferences, so just provide -# a sane default and let them override it at configure or make time. -VMWARE_USER_PATH = $(bindir)/vmware-user - AM_CPPFLAGS = -AM_CPPFLAGS += -DVMWARE_USER_PATH=\"$(VMWARE_USER_PATH)\" +AM_CPPFLAGS += -DVMTOOLSD_PATH=\"$(bindir)/vmtoolsd\" vmware_user_suid_wrapper_SOURCES = vmware_user_suid_wrapper_SOURCES += main.c diff --git a/open-vm-tools/vmware-user-suid-wrapper/locationsdb.c b/open-vm-tools/vmware-user-suid-wrapper/locationsdb.c deleted file mode 100644 index 8059eb739..000000000 --- a/open-vm-tools/vmware-user-suid-wrapper/locationsdb.c +++ /dev/null @@ -1,149 +0,0 @@ -/********************************************************* - * Copyright (C) 2007 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - - -/* - * locationsdb.c -- - * - * Provides the QueryLocationsDB routine for finding keys in the locations - * database. Because our application is a setuid binary and we want to - * minimize risk, we retain the duplicated functionality here rather than - * link against lib/unixinstall. - */ - -#if !defined(sun) && !defined(__FreeBSD__) && !defined(linux) -# error This program is not supported on your platform. -#endif - -#include - -#include -#include -#include - -#include "vm_basic_types.h" -#include "wrapper.h" - - - -/* - * Local data - */ - -/* - * Mappings between queries and search strings - */ - -typedef struct Mapping { - const char *answer; /* string to match for "answer FOO" */ - const char *remove; /* string to match for "remove_answer FOO" */ - size_t answerSize; /* size of answer buffer */ - size_t removeSize; /* size of remove buffer */ -} Mapping; - -/* - * queryMappings between Selector => search strings. Used by QueryLocationsDB. - */ - -#define ANSWER_LIBDIR "answer LIBDIR" -#define REMOVE_LIBDIR "remove_answer LIBDIR" -#define ANSWER_BINDIR "answer BINDIR" -#define REMOVE_BINDIR "remove_answer BINDIR" - -static Mapping queryMappings[] = { - { ANSWER_LIBDIR, REMOVE_LIBDIR, sizeof ANSWER_LIBDIR, sizeof REMOVE_LIBDIR }, - { ANSWER_BINDIR, REMOVE_BINDIR, sizeof ANSWER_BINDIR, sizeof REMOVE_BINDIR }, - { 0, 0, 0, 0 } -}; - - -/* - * Global functions (definitions) - */ - - -/* - *---------------------------------------------------------------------------- - * - * QueryLocationsDB -- - * - * Based on the caller's Selector, determines the directory selected as - * "LIBDIR", "BINDIR", etc. when the Tools were last configured. - * - * Results: - * TRUE on success, FALSE on failure. queryDir is filled in on success. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -Bool -QueryLocationsDB(const char *locations, // IN : path of locations database - Selector selector, // IN : DB query to search for - char *queryDir, // OUT: address to write dirname to - size_t queryDirSize) // IN : size of queryDir buffer -{ - FILE *file = NULL; - char buf[MAXPATHLEN]; - Bool found = FALSE; - Mapping *map; - - if (selector < 0 || selector >= QUERY_MAX) { - Error("Internal logic error. This is a bug."); - return FALSE; - } - - file = fopen(locations, "r"); - if (!file) { - return FALSE; - } - - map = &queryMappings[selector]; - - /* - * We need to inspect the entire locations database since there are both - * "answer"s and "remove_answer"s. We want to provide the last answer that - * has not been removed. - */ - while (fgets(buf, sizeof buf, file)) { - if (strncmp(buf, map->answer, map->answerSize - 1) == 0) { - char *newline; - - strncpy(queryDir, buf + map->answerSize, queryDirSize); - if (queryDir[queryDirSize - 1] != '\0') { - found = FALSE; - continue; - } - - /* Truncate the string at the newline character, if it's present. */ - newline = strchr(queryDir, '\n'); - if (newline && newline - queryDir < queryDirSize) { - *newline = '\0'; - } - - found = TRUE; - } else if (strncmp(buf, map->remove, map->removeSize - 1) == 0) { - found = FALSE; - } - } - - fclose(file); - return found; -} diff --git a/open-vm-tools/vmware-user-suid-wrapper/main.c b/open-vm-tools/vmware-user-suid-wrapper/main.c index a34c784ce..117be8ddb 100644 --- a/open-vm-tools/vmware-user-suid-wrapper/main.c +++ b/open-vm-tools/vmware-user-suid-wrapper/main.c @@ -430,7 +430,8 @@ StartVMwareUser(char *const envp[]) int fd = -1; int ret; char path[MAXPATHLEN]; - char *argv[4]; + char *argv[6]; + size_t idx = 0; if (!BuildExecPath(path, sizeof path)) { return FALSE; @@ -480,25 +481,25 @@ StartVMwareUser(char *const envp[]) * can't parse the descriptor to pass as an argument. We set up the * argument vector accordingly. */ - argv[0] = path; + argv[idx++] = path; + argv[idx++] = "-n"; + argv[idx++] = "vmusr"; if (fd < 0) { Error("could not open %s\n", VMBLOCK_DEVICE); - argv[1] = NULL; } else { char fdStr[8]; ret = snprintf(fdStr, sizeof fdStr, "%d", fd); if (ret == 0 || ret >= sizeof fdStr) { Error("could not parse file descriptor (%d)\n", fd); - argv[1] = NULL; } else { - argv[1] = "--blockFd"; - argv[2] = fdStr; - argv[3] = NULL; + argv[idx++] = "--blockFd"; + argv[idx++] = fdStr; } } + argv[idx++] = NULL; CompatExec(path, argv, envp); /* @@ -506,7 +507,7 @@ StartVMwareUser(char *const envp[]) * if CompatExec fails. */ Error("could not execute %s: %s\n", path, strerror(errno)); - exit(EXIT_FAILURE); + _exit(EXIT_FAILURE); } @@ -533,10 +534,10 @@ Bool BuildExecPath(char *execPath, // OUT: Buffer to store executable's path size_t execPathSize) // IN : size of execPath buffer { - if (execPathSize < sizeof VMWARE_USER_PATH) { + if (execPathSize < sizeof VMTOOLSD_PATH) { return FALSE; } - strcpy(execPath, VMWARE_USER_PATH); + strcpy(execPath, VMTOOLSD_PATH); return TRUE; } #endif // ifndef USES_LOCATIONS_DB diff --git a/open-vm-tools/vmware-user-suid-wrapper/wrapper-linux.c b/open-vm-tools/vmware-user-suid-wrapper/wrapper-linux.c index bf7e2fee8..5b5d4788b 100644 --- a/open-vm-tools/vmware-user-suid-wrapper/wrapper-linux.c +++ b/open-vm-tools/vmware-user-suid-wrapper/wrapper-linux.c @@ -74,16 +74,16 @@ BuildExecPath(char *execPath, // OUT: Buffer to store executable's pat * paths to all the other paths selected during Tools configuration. The * locations database file is only writable by root, so we can trust it. */ - if (!QueryLocationsDB(LOCATIONS_PATH, QUERY_BINDIR, tmpPath, sizeof tmpPath)) { - Error("could not obtain BINDIR\n"); + if (!QueryLocationsDB(LOCATIONS_PATH, QUERY_SBINDIR, tmpPath, sizeof tmpPath)) { + Error("could not obtain SBINDIR\n"); return FALSE; } - if (strlen(tmpPath) + strlen("/vmware-user-wrapper") + 1 > sizeof tmpPath) { + if (strlen(tmpPath) + strlen("/vmtoolsd") + 1 > sizeof tmpPath) { Error("could not construct program filename\n"); return FALSE; } - strcat(tmpPath, "/vmware-user-wrapper"); + strcat(tmpPath, "/vmtoolsd"); /* * From readlink(2), "The readlink() system call does not append a NUL diff --git a/open-vm-tools/vmware-user-suid-wrapper/wrapper.h b/open-vm-tools/vmware-user-suid-wrapper/wrapper.h index a6ed69995..6335b9926 100644 --- a/open-vm-tools/vmware-user-suid-wrapper/wrapper.h +++ b/open-vm-tools/vmware-user-suid-wrapper/wrapper.h @@ -57,14 +57,15 @@ */ typedef enum { - QUERY_LIBDIR = 0, /* Ask for "BINDIR" */ - QUERY_BINDIR, /* Ask for "LIBDIR" */ + QUERY_LIBDIR = 0, /* Ask for "LIBDIR" */ + QUERY_BINDIR, /* Ask for "BINDIR" */ + QUERY_SBINDIR, /* Ask for "SBINDIR" */ QUERY_MAX /* Upper limit -- Insert other queries above only. */ } Selector; #else -# ifndef VMWARE_USER_PATH -# error This program requires either USES_LOCATIONS_DB or VMWARE_USER_PATH. -# endif // ifndef VMWARE_USER_PATH +# ifndef VMTOOLSD_PATH +# error This program requires either USES_LOCATIONS_DB or VMTOOLSD_PATH. +# endif // ifndef VMTOOLSD_PATH #endif // ifdef USES_LOCATIONS_DB diff --git a/open-vm-tools/vmware-user/COPYING b/open-vm-tools/vmware-user/COPYING deleted file mode 100644 index 09f465ab7..000000000 --- a/open-vm-tools/vmware-user/COPYING +++ /dev/null @@ -1,502 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/open-vm-tools/vmware-user/Makefile.am b/open-vm-tools/vmware-user/Makefile.am deleted file mode 100644 index 484de3fd3..000000000 --- a/open-vm-tools/vmware-user/Makefile.am +++ /dev/null @@ -1,32 +0,0 @@ -################################################################################ -### Copyright 2007 VMware, Inc. All rights reserved. -### -### This program is free software; you can redistribute it and/or modify -### it under the terms of version 2 of the GNU General Public License as -### published by the Free Software Foundation. -### -### This program 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 program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -################################################################################ - -bin_PROGRAMS = vmware-user - -AM_CFLAGS = - -AM_CXXFLAGS = $(AM_CFLAGS) - -vmware_user_SOURCES = -vmware_user_SOURCES += vmware-user.cpp - -install-exec-hook: - $(INSTALL) -d $(DESTDIR)$(datadir)/applications/ - $(INSTALL) -m 644 $(top_srcdir)/scripts/common/vmware-user.desktop \ - $(DESTDIR)$(datadir)/applications/ -uninstall-hook: - rm -f $(DESTDIR)$(datadir)/applications/vmware-user.desktop diff --git a/open-vm-tools/vmware-user/copyPaste.c b/open-vm-tools/vmware-user/copyPaste.c deleted file mode 100644 index 12b1716df..000000000 --- a/open-vm-tools/vmware-user/copyPaste.c +++ /dev/null @@ -1,2102 +0,0 @@ -/********************************************************* - * Copyright (C) 2005 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - -/* - * copyPaste.c -- - * - * Set of functions in guest side for copy/paste (both file and text). - * Currently there are 2 versions copy/paste. Version 1 only supports - * text copy/paste, and based on backdoor cmd. Version 2 supports both - * text and file copy/paste, and based on guestRPC. - * - * G->H Text Copy/Paste (version 1) - * -------------------- - * When Ungrab, CopyPaste_RequestSelection got called, which try to get - * selection text and send to backdoor. - * - * H->G Text Copy/Paste (version 1) - * -------------------- - * When grab, CopyPaste_GetBackdoorSelections got called, which first - * get host selection text, then claim as selection owner. If some app - * asks for selection, CopyPasteSelectionGetCB will reply with host - * selection text. - * - * G->H Copy/Paste (version 2) - * -------------------- - * When Ungrab, host vmx will send out rpc command "copypaste.gh.data.get", - * which handler is CopyPasteRpcInGHSetDataCB. It first tries to get selection - * contents and sends it back as rpc result. For file transfer, host vmx - * will transfer files with hgFileCopy lib. Function CopyPasteGHFileListGetNext - * will handle the file list during transfer. - * - * H->G Copy/Paste (version 2) - * -------------------- - * When grab, host vmx will send host selection content to guest with - * out rpc command "copypaste.hg.data.set", which handler is - * CopyPasteRpcInHGSetDataCB. It first keeps a copy then claim as selection - * owner. If some app asks for files, CopyPasteSelectionGetCB will ask host - * vmx to transfer files into a temp dir with rpc "copypaste.hgCopyFiles", - * then reply with host selection file list. For KDE and gnome the file list - * format is different. If someapp asks for text, CopyPasteSelectionGetCB - * will provide the data. - */ - -#include "vmwareuserInt.h" -#include -#include -#include -#include -#include - -#include "vm_assert.h" -#include "debug.h" -#include "str.h" -#include "strutil.h" -#include "eventManager.h" -#include "guestApp.h" -#include "dnd.h" -#include "util.h" -#include "cpName.h" -#include "cpNameUtil.h" -#include "guestInfoLib.h" -#include "vmblock.h" -#include "file.h" -#include "codeset.h" -#include "escape.h" -#include "hostinfo.h" -#include "wiper.h" -#include "vmware/guestrpc/tclodefs.h" - -/* - * Gtk 1.2 doesn't know about the CLIPBOARD selection, but that doesn't matter, we - * just create the atom we need directly in main(). - */ -#ifndef GDK_SELECTION_CLIPBOARD -GdkAtom GDK_SELECTION_CLIPBOARD; -#endif - -#ifndef GDK_SELECTION_TYPE_TIMESTAMP -GdkAtom GDK_SELECTION_TYPE_TIMESTAMP; -#endif - -#ifndef GDK_SELECTION_TYPE_UTF8_STRING -GdkAtom GDK_SELECTION_TYPE_UTF8_STRING; -#endif - -typedef struct FCPGHState { - char *fileList; - char *fileListNext; - size_t fileListSize; -} FCPGHState; - -/* - * Currently there are 2 versions copy/paste. - * Key points in copy/paste version 1: - * 1. Only text copy/paste - * 2. copy/paste is based on backdoor directly - * - * Key points in copy/paste version 2: - * 1. Support both file/text copy/paste - * 2. Both file/text copy/paste are based on guestRPC - */ -static int32 gVmxCopyPasteVersion = 1; - -/* - * Getting a selection is an asyncronous event, so we have to keep track of both - * selections globablly in order to decide which one to use. - */ -static Bool gWaitingOnGuestSelection = FALSE; -static char gGuestSelPrimaryBuf[MAX_SELECTION_BUFFER_LENGTH]; -static char gGuestSelClipboardBuf[MAX_SELECTION_BUFFER_LENGTH]; -static uint64 gGuestSelPrimaryTime = 0; -static uint64 gGuestSelClipboardTime = 0; -static char gHostClipboardBuf[MAX_SELECTION_BUFFER_LENGTH]; - -/* Guest->Host state. */ -FCPGHState gFcpGHState; -/* RPC buffer for Guest->Host FCP. */ -static char *gGHFCPRpcResultBuffer; -/* File list size for Guest->Host FCP. */ -static size_t gGHFCPListSize; -static Bool gHGFCPPending; -/* Current selection is text or file list (for FCP). */ -static Bool gHGIsClipboardFCP; -/* - * Total file size in selection list. This is used to check if there is enough - * space in guest OS for host->guest file transfer. - */ -static uint64 gHGFCPTotalSize; - -static GdkAtom gFCPAtom[NR_FCP_TARGETS]; - -/* Host->guest file transfer status, used for sync between transfer and paste. */ -int gHGFCPFileTransferStatus; - -static char gFileRoot[DND_MAX_PATH]; -static size_t gFileRootSize; -static Bool gIsOwner; -static VmTimeType gHGGetListTime; - -/* - * Forward Declarations - */ -static INLINE void CopyPasteStateInit(void); -static void CopyPasteSelectionReceivedCB(GtkWidget *widget, - GtkSelectionData *selection_data, - gpointer data); -static void CopyPasteSelectionGetCB(GtkWidget *widget, - GtkSelectionData *selection_data, - guint info, - guint time_stamp, - gpointer data); -static gint CopyPasteSelectionClearCB(GtkWidget *widget, - GdkEventSelection *event, - gpointer data); - -static void CopyPasteSetBackdoorSelections(void); -static Bool CopyPasteRpcInGHSetDataCB(char const **result, size_t *resultLen, - const char *name, const char *args, - size_t argsSize,void *clientData); -static Bool CopyPasteRpcInHGSetDataCB(char const **result, size_t *resultLen, - const char *name, const char *args, - size_t argsSize,void *clientData); - -static INLINE void CopyPasteGHFileListClear(void); -static INLINE void CopyPasteGHFileListSet(char *fileList, size_t fileListSize); - -/* This struct is only used by CopyPasteSelectionRemoveTarget. */ -struct SelectionTargetList { - GdkAtom selection; - GtkTargetList *list; -}; - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteSelectionRemoveTarget -- - * - * To remove a target from a selection target list. The reason to develop - * this function is that in gtk there is only gtk_selection_add_target to - * add supported target to selection list, but no function to remove one. - * - * Results: - * None. - * - * Side effects: - * If no more target, the selection list will be removed too. - * - *----------------------------------------------------------------------------- - */ - -void -CopyPasteSelectionRemoveTarget(GtkWidget *widget, - GdkAtom selection, - GdkAtom target) -{ - const char *selection_handler_key = "gtk-selection-handlers"; - struct SelectionTargetList *targetList; - GList *tempList; - GList *selectionLists; - - /* Get selection list. */ - selectionLists = gtk_object_get_data(GTK_OBJECT (widget), selection_handler_key); - tempList = selectionLists; - while (tempList) { - /* Enumerate the list to find the selection. */ - targetList = tempList->data; - if (targetList->selection == selection) { - /* Remove target. */ - gtk_target_list_remove(targetList->list, target); - /* If no more target, remove selection from list. */ - if (!targetList->list->list) { - /* Free target list. */ - gtk_target_list_unref(targetList->list); - g_free(targetList); - /* Remove and free selection node. */ - selectionLists = g_list_remove_link(selectionLists, tempList); - g_list_free_1(tempList); - } - break; - } - tempList = tempList->next; - } - /* Put new selection list back. */ - gtk_object_set_data (GTK_OBJECT (widget), selection_handler_key, selectionLists); -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPaste_RequestSelection -- - * - * Request the guest's text clipboard (asynchronously), we'll give it to - * the host when the request completes. For version 1 guest->host text - * copy/paste. - * - * Results: - * None. - * - * Side effects: - * The owner of the clipboard will get a request from our application. - * - *----------------------------------------------------------------------------- - */ - -void -CopyPaste_RequestSelection(void) -{ - if (gVmxCopyPasteVersion > 1) { - return; - } - - /* - * Ask for both the PRIMARY and CLIPBOARD selections. - */ - gGuestSelPrimaryBuf[0] = '\0'; - gGuestSelClipboardBuf[0] = '\0'; - - /* Only send out request if we are not the owner. */ - if (!gIsOwner) { - /* Try to get utf8 text from primary and clipboard. */ - gWaitingOnGuestSelection = TRUE; - gtk_selection_convert(gUserMainWidget, - GDK_SELECTION_PRIMARY, - GDK_SELECTION_TYPE_UTF8_STRING, - GDK_CURRENT_TIME); - while (gWaitingOnGuestSelection) gtk_main_iteration(); - - gWaitingOnGuestSelection = TRUE; - gtk_selection_convert(gUserMainWidget, - GDK_SELECTION_CLIPBOARD, - GDK_SELECTION_TYPE_UTF8_STRING, - GDK_CURRENT_TIME); - while (gWaitingOnGuestSelection) gtk_main_iteration(); - - if (gGuestSelPrimaryBuf[0] == '\0' && gGuestSelClipboardBuf[0] == '\0') { - /* - * If we cannot get utf8 text, try to get localized text from primary - * and clipboard. - */ - gWaitingOnGuestSelection = TRUE; - gtk_selection_convert(gUserMainWidget, - GDK_SELECTION_PRIMARY, - GDK_SELECTION_TYPE_STRING, - GDK_CURRENT_TIME); - while (gWaitingOnGuestSelection) gtk_main_iteration(); - - gWaitingOnGuestSelection = TRUE; - gtk_selection_convert(gUserMainWidget, - GDK_SELECTION_CLIPBOARD, - GDK_SELECTION_TYPE_STRING, - GDK_CURRENT_TIME); - while (gWaitingOnGuestSelection) gtk_main_iteration(); - } - } - /* Send text to host. */ - Debug("CopyPaste_RequestSelection: Prim is [%s], Clip is [%s]\n", - gGuestSelPrimaryBuf, gGuestSelClipboardBuf); - CopyPasteSetBackdoorSelections(); -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteSelectionReceivedCB -- - * - * Callback for the gtk signal "selection_recieved". - * Called because we previously requested a copy/paste selection and - * finally got results of that asynchronous operation. After some basic - * sanity checks, send the result (in selection_data) thru the backdoor - * (version 1) or guestRPC (version 2) so the vmx can copy it to host - * clipboard. - * - * We made several requests for selections, the string (actual data) and - * file list for each of PRIMARY and CLIPBOARD selections. So this funtion - * will get called several times, once for each request. - * - * For guest->host copy/paste (both text and file). - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -void -CopyPasteSelectionReceivedCB(GtkWidget *widget, // IN: unused - GtkSelectionData *selection_data, // IN: requested data - gpointer data) // IN: unused -{ - char *target; - char *utf8Str = NULL; - size_t len; - size_t aligned_len; - - if ((widget == NULL) || (selection_data == NULL)) { - Debug("CopyPasteSelectionReceivedCB: Error, widget or selection_data is invalid\n"); - goto exit; - } - - if (selection_data->length < 0) { - Debug("CopyPasteSelectionReceivedCB: Error, length less than 0\n"); - goto exit; - } - - /* Try to get clipboard or selection timestamp. */ - if (selection_data->target == GDK_SELECTION_TYPE_TIMESTAMP) { - if (selection_data->selection == GDK_SELECTION_PRIMARY) { - if (selection_data->length == 4) { - gGuestSelPrimaryTime = *(uint32 *)selection_data->data; - Debug("CopyPasteSelectionReceivedCB: Got pri time [%"FMT64"u]\n", - gGuestSelPrimaryTime); - } else if (selection_data->length == 8) { - gGuestSelPrimaryTime = *(uint64 *)selection_data->data; - Debug("CopyPasteSelectionReceivedCB: Got pri time [%"FMT64"u]\n", - gGuestSelPrimaryTime); - } else { - Debug("CopyPasteSelectionReceivedCB: Unknown pri time. Size %d\n", - selection_data->length); - } - } - if (selection_data->selection == GDK_SELECTION_CLIPBOARD) { - if (selection_data->length == 4) { - gGuestSelClipboardTime = *(uint32 *)selection_data->data; - Debug("CopyPasteSelectionReceivedCB: Got clip time [%"FMT64"u]\n", - gGuestSelClipboardTime); - } else if (selection_data->length == 8) { - gGuestSelClipboardTime = *(uint64 *)selection_data->data; - Debug("CopyPasteSelectionReceivedCB: Got clip time [%"FMT64"u]\n", - gGuestSelClipboardTime); - } else { - Debug("CopyPasteSelectionReceivedCB: Unknown clip time. Size %d\n", - selection_data->length); - } - } - goto exit; - } - - if (selection_data->selection == GDK_SELECTION_PRIMARY) { - target = gGuestSelPrimaryBuf; - } else if (selection_data->selection == GDK_SELECTION_CLIPBOARD) { - target = gGuestSelClipboardBuf; - } else { - goto exit; - } - - utf8Str = selection_data->data; - len = strlen(selection_data->data); - - if (selection_data->target != GDK_SELECTION_TYPE_STRING && - selection_data->target != GDK_SELECTION_TYPE_UTF8_STRING) { - /* It is a file list. */ - if (len >= MAX_SELECTION_BUFFER_LENGTH - 1) { - Warning("CopyPasteSelectionReceivedCB file list too long\n"); - } else { - memcpy(target, selection_data->data, len + 1); - } - goto exit; - } - - /* - * If target is GDK_SELECTION_TYPE_STRING, assume encoding is local code - * set. Convert to utf8 before send to vmx. - */ - if (selection_data->target == GDK_SELECTION_TYPE_STRING && - !CodeSet_CurrentToUtf8(selection_data->data, - selection_data->length, - &utf8Str, - &len)) { - Debug("CopyPasteSelectionReceivedCB: Couldn't convert to utf8 code set\n"); - gWaitingOnGuestSelection = FALSE; - return; - } - - /* - * String in backdoor communication is 4 bytes by 4 bytes, so the len - * should be aligned to 4; - */ - aligned_len = (len + 4) & ~3; - if (aligned_len >= MAX_SELECTION_BUFFER_LENGTH) { - /* With alignment, len is still possible to be less than max. */ - if (len < (MAX_SELECTION_BUFFER_LENGTH - 1)) { - memcpy(target, utf8Str, len + 1); - } else { - memcpy(target, utf8Str, MAX_SELECTION_BUFFER_LENGTH - 1); - target[MAX_SELECTION_BUFFER_LENGTH - 1] ='\0'; - } - } else { - memcpy(target, utf8Str, len + 1); - } - -exit: - if (selection_data->target == GDK_SELECTION_TYPE_STRING) { - free(utf8Str); - } - gWaitingOnGuestSelection = FALSE; -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteSelectionGetCB -- - * - * Callback for the gtk signal "selection_get". - * This is called when some other app requests the copy/paste selection, - * probably because we declare oursleves the selection owner on mouse - * grab. In text copy/paste case, we simply respond with contents of - * gHostClipboardBuf, which should have been set on mouse grab. In file - * copy/paste case, send file transfer request to host vmx, then return - * file list with right format according to different request. - * For host->guest copy/paste (both text and file). - * - * Results: - * None - * - * Side effects: - * An X message is sent to the requesting app containing the data, it - * will likely act on it in some way. In FCP case, may first start a - * host->guest file transfer. Add block if blocking driver is available, - * otherwise wait till file copy done. - * - *----------------------------------------------------------------------------- - */ - -void -CopyPasteSelectionGetCB(GtkWidget *widget, // IN: unused - GtkSelectionData *selection_data, // IN: requested type - // OUT:the data to be sent - guint info, // IN: unused - guint time_stamp, // IN: unsued - gpointer data) // IN: unused -{ - const char *begin = NULL; - const char *end = NULL; - const char *next = NULL; - const char *pre = NULL; - const char *post = NULL; - size_t preLen = 0; - size_t postLen = 0; - int len = 0; - char *text = NULL; - size_t textLen = 1; - Bool blockAdded = FALSE; - Bool gnomeFCP = FALSE; - VmTimeType curTime; - - if ((widget == NULL) || (selection_data == NULL)) { - Debug("CopyPasteSelectionGetCB: Error, widget or selection_data is invalid\n"); - return; - } - - /* If it is text copy paste, return gHostClipboardBuf. */ - if (GDK_SELECTION_TYPE_STRING == selection_data->target || - GDK_SELECTION_TYPE_UTF8_STRING == selection_data->target) { - char *outBuf = gHostClipboardBuf; - size_t len = strlen(gHostClipboardBuf); - - /* - * If target is GDK_SELECTION_TYPE_STRING, assume encoding is local code - * set. Convert from utf8 to local one. - */ - if (GDK_SELECTION_TYPE_STRING == selection_data->target && - !CodeSet_Utf8ToCurrent(gHostClipboardBuf, - strlen(gHostClipboardBuf), - &outBuf, - &len)) { - Debug("CopyPasteSelectionGetCB: can not convert to current codeset\n"); - return; - } - - gtk_selection_data_set(selection_data, selection_data->target, 8, - outBuf, len); - Debug("CopyPasteSelectionGetCB: Set text [%s]\n", outBuf); - - if (GDK_SELECTION_TYPE_STRING == selection_data->target) { - free(outBuf); - } - - return; - } - - if (selection_data->target != gFCPAtom[FCP_TARGET_INFO_URI_LIST] && - selection_data->target != gFCPAtom[FCP_TARGET_INFO_GNOME_COPIED_FILES]) { - Debug("CopyPasteSelectionGetCB: Got unknown target\n"); - return; - } - - if (!gHGIsClipboardFCP) { - Debug("CopyPasteSelectionGetCB: no file list available\n"); - return; - } - - /* - * KDE may ask for clipboard content right after clipboard owner changed, - * and cause unexpected HG file copy. So HG FCP will return nothing for 1 - * second after switch from host OS to guest OS. Please refer to bug 301971. - */ - Hostinfo_GetTimeOfDay(&curTime); - if (curTime - gHGGetListTime < FCP_COPY_DELAY) { - Debug("%s: waiting for delay\n", __FUNCTION__); - return; - } - - if (gHGFCPFileTransferStatus == FCP_FILE_TRANSFER_NOT_YET) { - if (GuestInfo_GetAvailableDiskSpace(gFileRoot) < gHGFCPTotalSize) { - Debug("CopyPasteSelectionGetCB no enough space to copy file from host.\n"); - return; - } - /* Send host a rpc to start file transfer. */ - if (!GuestApp_RpcSendOneCPName("copypaste.hg.copy.files", ' ', - gFileRoot, gFileRootSize)) { - Debug("CopyPasteSelectionGetCB: failed sending copypaste.hg.copy.files " - "with CPName"); - return; - } - gHGFCPFileTransferStatus = FCP_FILE_TRANSFERRING; - } - - if (DnD_BlockIsReady(&gBlockCtrl)) { - /* Add a block on the staging directory for this command. */ - if (gBlockCtrl.AddBlock(gBlockCtrl.fd, gFileRoot)) { - Debug("CopyPasteSelectionGetCB: add block [%s].\n", gFileRoot); - blockAdded = TRUE; - } else { - Warning("CopyPasteSelectionGetCB: Unable to add block [%s].\n", gFileRoot); - } - } - - if (!blockAdded) { - /* - * If there is no blocking driver, wait here till file copy is done. - * 2 reasons to keep this: - * 1. If run vmware-user stand-alone as non-root, blocking driver can not - * be opened. Debug purpose only. - * 2. Other platforms (Solaris, FreeBSD, etc) may also use this code, and there - * is no blocking driver yet. - */ - Debug("CopyPasteSelectionGetCB no blocking driver, waiting for " - "HG file copy done ...\n"); - while (gHGFCPFileTransferStatus != FCP_FILE_TRANSFERRED) { - struct timeval tv; - int nr; - - tv.tv_sec = 0; - nr = EventManager_ProcessNext(gEventQueue, (uint64 *)&tv.tv_usec); - if (nr != 1) { - Debug("CopyPasteSelectionGetCB unexpected end of loop: returned " - "value is %d.\n", nr); - return; - } - if (select(0, NULL, NULL, NULL, &tv) == -1) { - Debug("CopyPasteSelectionGetCB error in select (%s).\n", - strerror(errno)); - return; - } - } - - Debug("CopyPasteSelectionGetCB file transfer done!\n"); - } - - /* Setup the format string components */ - if (selection_data->target == gFCPAtom[FCP_TARGET_INFO_URI_LIST]) { - Debug("CopyPasteSelectionGetCB Got uri_list request!\n"); - pre = DND_URI_LIST_PRE_KDE; - preLen = sizeof DND_URI_LIST_PRE_KDE - 1; - post = DND_URI_LIST_POST; - postLen = sizeof DND_URI_LIST_POST - 1; - } - if (selection_data->target == gFCPAtom[FCP_TARGET_INFO_GNOME_COPIED_FILES]) { - Debug("CopyPasteSelectionGetCB Got gnome_copied request!\n"); - pre = FCP_GNOME_LIST_PRE; - preLen = sizeof FCP_GNOME_LIST_PRE - 1; - post = FCP_GNOME_LIST_POST; - postLen = sizeof FCP_GNOME_LIST_POST - 1; - gnomeFCP = TRUE; - - textLen += 5; - text = Util_SafeRealloc(text, textLen); - Str_Snprintf(text, 6, "copy\n"); - } - - if (!pre) { - Debug("CopyPasteSelectionGetCB: invalid drag target info\n"); - return; - } - - - /* - * Set begin to first non-NUL character and end to last NUL character to - * prevent errors in calling CPName_GetComponent(). - */ - for(begin = gHostClipboardBuf; *begin == '\0'; begin++) - ; - end = CPNameUtil_Strrchr(gHostClipboardBuf, gGHFCPListSize + 1, '\0'); - ASSERT(end); - - /* Build up selection data */ - while ((len = CPName_GetComponent(begin, end, &next)) != 0) { - const size_t origTextLen = textLen; - Bool freeBegin = FALSE; - - if (len < 0) { - Debug("CopyPasteSelectionGetCB: error getting next component\n"); - if (text) { - free(text); - } - return; - } - - /* - * A URI list will expect the provided path to be escaped. If we cannot - * escape the path for some reason we just use the unescaped version and - * hope that it works. - */ - if (selection_data->target == gFCPAtom[FCP_TARGET_INFO_URI_LIST]) { - size_t newLen; - char *escapedComponent; - int escIndex; - int bytesToEsc[256] = { 0, }; - - /* We escape the following characters based on RFC 1630. */ - bytesToEsc['#'] = 1; - bytesToEsc['?'] = 1; - bytesToEsc['*'] = 1; - bytesToEsc['!'] = 1; - bytesToEsc['%'] = 1; /* Escape character */ - - /* Escape non-ASCII characters so we can pass UTF-8 filenames */ - for (escIndex = 0x80; escIndex < 0x100; escIndex++) { - bytesToEsc[escIndex] = 1; - } - - escapedComponent = Escape_Do('%', bytesToEsc, begin, len, &newLen); - if (escapedComponent) { - begin = escapedComponent; - len = newLen; - freeBegin = TRUE; - } - } - - /* - * Append component. NUL terminator was accounted for by initializing - * textLen to one above. - */ - textLen += preLen + len + postLen; - text = Util_SafeRealloc(text, textLen); - - /* - * Bug 143147: Gnome FCP does not like the trailing newlines. We don't - * have this problem for targets that ask for URI lists. So we don't see - * this problem on: - * - KDE which asks for URI lists during FCP - * - DnD in both Gnome and KDE since they ask for URI lists. - * - * This is a problem only for Gnome FCP which expects a specially - * formatted 'copy' command string containing the file list which it then - * converts into a URI list internally. - */ - Str_Snprintf(text + origTextLen - 1, - textLen - origTextLen + 1, - "%s%s%s", pre, begin, gnomeFCP && next == end ? "" : post); - - if (freeBegin) { - free((void *)begin); - } - - /* Iterate to next component */ - begin = next; - } - - /* - * Send out the data using the selection system. When sending a string, GTK will - * ensure that a null terminating byte is added to the end so we do not need to - * add it. GTK also copies the data so the original will never be modified. - */ - Debug("CopyPasteSelectionGetCB: set file list [%s]\n", text); - gtk_selection_data_set(selection_data, selection_data->target, - 8, /* 8 bits per character. */ - text, textLen); - free(text); -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteSelectionClearCB -- - * - * Callback for the gtk signal "selection_clear". - * - * Results: - * Always TRUE. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -static gint -CopyPasteSelectionClearCB(GtkWidget *widget, // IN: unused - GdkEventSelection *event, // IN: unused - gpointer data) // IN: unused -{ - Debug("CopyPasteSelectionClearCB got clear signal\n"); - gIsOwner = FALSE; - return TRUE; -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteSetBackdoorSelections -- - * - * Set the clipboard one of two ways, the old way or the new way. - * The old way uses GuestApp_SetSel and there's only one selection. - * Set backdoor selection with either primary selection or clipboard. - * The primary selection is the first priority, then clipboard. - * If both unavailable, set backdoor selection length to be 0. - * This will be used by older VMXs or VMXs on Windows hosts (which - * has only one clipboard). Doing this gives us backwards - * compatibility. - * - * The new way uses new sets both PRIMARY and CLIPBOARD. Newer Linux - * VMXs will use these rather than the above method and have the two - * selections set separately. - * - * XXX: The "new way" doesn't exist yet, the vmx has no support for it. - * - * Results: - * None. - * - * Side effects: - * The VMX probably changes some string buffers. - * - *----------------------------------------------------------------------------- - */ - -void -CopyPasteSetBackdoorSelections(void) -{ - uint32 const *p; - size_t len; - size_t aligned_len; - size_t primaryLen; - size_t clipboardLen; - unsigned int i; - - primaryLen = strlen(gGuestSelPrimaryBuf); - clipboardLen = strlen(gGuestSelClipboardBuf); - - if (primaryLen) { - /* - * Send primary selection to backdoor if it exists. - */ - p = (uint32 const *)gGuestSelPrimaryBuf; - } else if (clipboardLen) { - /* - * Otherwise send clipboard to backdoor if it exists. - */ - p = (uint32 const *)gGuestSelClipboardBuf; - } else { - /* - * Neither selection is set - */ - p = NULL; - } - - if (p == NULL) { - GuestApp_SetSelLength(0); - Debug("CopyPasteSetBackdoorSelections Set empty text.\n"); - } else { - len = strlen((char *)p); - Debug("CopyPasteSetBackdoorSelections Set text [%s].\n", (char *)p); - aligned_len = (len + 4) & ~3; - - /* Here long string should already be truncated. */ - ASSERT(aligned_len <= MAX_SELECTION_BUFFER_LENGTH); - - GuestApp_SetSelLength(len); - for (i = 0; i < len; i += 4, p++) { - GuestApp_SetNextPiece(*p); - } - } -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPaste_GetBackdoorSelections -- - * - * Get the clipboard "the old way". - * The old way uses GuestApp_SetSel and there's only one selection. - * We don't have to do anything for the "new way", since the host - * will just push PRIMARY and/or CLIPBOARD when they are available - * on the host. - * - * XXX: the "new way" isn't availble yet because the vmx doesn't - * implement separate clipboards. Even when it does this - * function will still exist for backward compatibility - * - * Results: - * TRUE if selection length>=0, FLASE otherwise. - * - * Side effects: - * This application becomes the selection owner for PRIMARY and/or - CLIPBOARD selections. - * - *----------------------------------------------------------------------------- - */ - -Bool -CopyPaste_GetBackdoorSelections(void) -{ - int selLength; - int iAtom; - - if (gVmxCopyPasteVersion > 1) { - return TRUE; - } - - selLength = GuestApp_GetHostSelectionLen(); - if (selLength < 0) { - return FALSE; - } else if (selLength > 0) { - memset(gHostClipboardBuf, 0, sizeof (gHostClipboardBuf)); - GuestApp_GetHostSelection(selLength, gHostClipboardBuf); - Debug("CopyPaste_GetBackdoorSelections Get text [%s].\n", gHostClipboardBuf); - gtk_selection_owner_set(gUserMainWidget, - GDK_SELECTION_CLIPBOARD, - GDK_CURRENT_TIME); - gtk_selection_owner_set(gUserMainWidget, - GDK_SELECTION_PRIMARY, - GDK_CURRENT_TIME); - gIsOwner = TRUE; - gHGIsClipboardFCP = FALSE; - for (iAtom = 0; iAtom < NR_FCP_TARGETS; iAtom++) { - CopyPasteSelectionRemoveTarget(gUserMainWidget, - GDK_SELECTION_PRIMARY, - gFCPAtom[iAtom]); - CopyPasteSelectionRemoveTarget(gUserMainWidget, - GDK_SELECTION_CLIPBOARD, - gFCPAtom[iAtom]); - } - } - return TRUE; -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteRpcInGHSetDataCB -- - * - * Handler function for the "copypaste.gh.data.get" RPC command. Host is - * asking for clipboard contents for guest->host copy/paste. If both primary - * selection and clipboard are empty, the empty list should also be sent back - * because Host should release clipboard owner. - * - * For Guest->Host copy/paste operations only. - * - * Results: - * TRUE on success, FALSE on failure. - * - * Side effects: - * The owner of the clipboard will get requests from our application. - * - *----------------------------------------------------------------------------- - */ - -static Bool -CopyPasteRpcInGHSetDataCB(char const **result, // OUT - size_t *resultLen, // OUT - const char *name, // IN - const char *args, // IN - size_t argsSize, // Ignored - void *clientData) // Ignored -{ - int iAtom; - GdkAtom activeSelection = GDK_SELECTION_PRIMARY; - char *source = gGuestSelPrimaryBuf; - char format[256]; - char *rpcBody = NULL; - size_t rpcBodySize = 0; - - gGuestSelPrimaryBuf[0] = '\0'; - gGuestSelClipboardBuf[0] = '\0'; - - if (gIsOwner) { - Debug("CopyPasteRpcInGHSetDataCB Send empty buf to host\n"); - return RpcIn_SetRetVals(result, resultLen, "", TRUE); - } - - /* First check which one is newer, primary selection or clipboard. */ - gGuestSelPrimaryTime = 0; - gGuestSelClipboardTime = 0; - - gWaitingOnGuestSelection = TRUE; - gtk_selection_convert(gUserMainWidget, - GDK_SELECTION_PRIMARY, - GDK_SELECTION_TYPE_TIMESTAMP, - GDK_CURRENT_TIME); - while (gWaitingOnGuestSelection) gtk_main_iteration(); - - gWaitingOnGuestSelection = TRUE; - gtk_selection_convert(gUserMainWidget, - GDK_SELECTION_CLIPBOARD, - GDK_SELECTION_TYPE_TIMESTAMP, - GDK_CURRENT_TIME); - while (gWaitingOnGuestSelection) gtk_main_iteration(); - - if (gGuestSelPrimaryTime < gGuestSelClipboardTime) { - activeSelection = GDK_SELECTION_CLIPBOARD; - source = gGuestSelClipboardBuf; - } - -try_again: - /* Check if it is file list in the active selection. */ - for (iAtom = 0; iAtom < NR_FCP_TARGETS; iAtom++) { - gWaitingOnGuestSelection = TRUE; - gtk_selection_convert(gUserMainWidget, - activeSelection, - gFCPAtom[iAtom], - GDK_CURRENT_TIME); - while (gWaitingOnGuestSelection) gtk_main_iteration(); - if (source[0] != '\0') { - if (gVmxCopyPasteVersion < 2) { - /* Only vmx version greater than 2 support file copy/paste. */ - Debug("CopyPasteRpcInGHSetDataCB invalid operation\n"); - return RpcIn_SetRetVals(result, resultLen, - "invalid operation", FALSE); - } - break; - } - } - - if (source[0] != '\0') { - char *currName; - size_t currSize; - size_t index = 0; - char *ghFileList = NULL; - size_t ghFileListSize = 0; - - /* - * In gnome, before file list there may be a extra line indicating it - * is a copy or cut. - */ - if (strncmp(source, "copy", 4) == 0) { - source += 4; - } - if (strncmp(source, "cut", 3) == 0) { - source += 3; - } - - while (*source == '\n' || *source == '\r' || *source == ' ') { - source++; - } - - /* - * Get the the full filenames and last components from the URI list. The - * body of the RPC message will be these last components delimited with - * NUL characters; the Guest->Host file list will be the full paths - * delimited by NUL characters. - */ - while ((currName = DnD_UriListGetNextFile(source, - &index, - &currSize))) { - size_t lastComponentSize; - char *lastComponentStart; - - /* Append current filename to Guest->Host list */ - ghFileList = Util_SafeRealloc(ghFileList, - ghFileListSize + currSize + 1); - memcpy(ghFileList + ghFileListSize, currName, currSize); - ghFileListSize += currSize; - ghFileList[ghFileListSize] = '\0'; - ghFileListSize++; - - /* Append last component to RPC body */ - lastComponentStart = CPNameUtil_Strrchr(currName, currSize, DIRSEPC); - if (!lastComponentStart) { - /* - * This shouldn't happen since filenames are absolute, but handle - * it as if the file name is the last component - */ - lastComponentStart = currName; - } else { - /* Skip the last directory separator */ - lastComponentStart++; - } - - lastComponentSize = currName + currSize - lastComponentStart; - rpcBody = Util_SafeRealloc(rpcBody, rpcBodySize + lastComponentSize + 1); - memcpy(rpcBody + rpcBodySize, lastComponentStart, lastComponentSize); - rpcBodySize += lastComponentSize; - rpcBody[rpcBodySize] = '\0'; - rpcBodySize++; - - free(currName); - } - - if (!ghFileList || !rpcBody) { - Warning("CopyPasteRpcInGHSetDataCB: no filenames retrieved " - "from URI list\n"); - free(ghFileList); - free(rpcBody); - return RpcIn_SetRetVals(result, resultLen, - "error retrieving file name", FALSE); - } - - /* Set the list of full paths */ - CopyPasteGHFileListSet(ghFileList, ghFileListSize); - - /* rpcBody (and its size) will always contain a trailing NUL character */ - rpcBodySize--; - Debug("CopyPasteRpcInGHSetDataCB: Sending: [%s] (%zu)\n", - CPName_Print(rpcBody, rpcBodySize), rpcBodySize); - - Str_Sprintf(format, sizeof format, "%d ", CPFORMAT_FILELIST); - *resultLen = rpcBodySize + strlen(format); - - free(gGHFCPRpcResultBuffer); - gGHFCPRpcResultBuffer = Util_SafeCalloc(1, rpcBodySize + strlen(format)); - - memcpy(gGHFCPRpcResultBuffer, format, strlen(format)); - memcpy(gGHFCPRpcResultBuffer + strlen(format), rpcBody, rpcBodySize); - free(rpcBody); - *result = gGHFCPRpcResultBuffer; - return TRUE; - } else { - /* Try to get utf8 text from active selection. */ - gWaitingOnGuestSelection = TRUE; - gtk_selection_convert(gUserMainWidget, - activeSelection, - GDK_SELECTION_TYPE_UTF8_STRING, - GDK_CURRENT_TIME); - while (gWaitingOnGuestSelection) gtk_main_iteration(); - - if (source[0] == '\0') { - /* Try to get text from active selection. */ - gWaitingOnGuestSelection = TRUE; - gtk_selection_convert(gUserMainWidget, - activeSelection, - GDK_SELECTION_TYPE_STRING, - GDK_CURRENT_TIME); - while (gWaitingOnGuestSelection) gtk_main_iteration(); - } - - /* - * With 'cut' operation OpenOffice will put data into clipboard but - * set same timestamp for both clipboard and primary selection. - * If primary timestamp is same as clipboard timestamp, we should try - * clipboard again if primary selection is empty. For details please - * refer to bug 300780. - */ - if (source[0] == '\0' && - gGuestSelPrimaryTime == gGuestSelClipboardTime && - gGuestSelPrimaryTime != 0) { - gGuestSelPrimaryTime = 0; - gGuestSelClipboardTime = 0; - activeSelection = GDK_SELECTION_CLIPBOARD; - source = gGuestSelClipboardBuf; - goto try_again; - } - - if (source[0] != '\0') { - free(gGHFCPRpcResultBuffer); - - gGHFCPRpcResultBuffer = - Str_Asprintf(NULL, "%d %s", CPFORMAT_TEXT, source); - - if (!gGHFCPRpcResultBuffer) { - Debug("CopyPasteRpcInGHSetDataCB failed to alloc memory.\n"); - return RpcIn_SetRetVals(result, resultLen, - "error allocating memory", FALSE); - } - - *result = gGHFCPRpcResultBuffer; - *resultLen = strlen(gGHFCPRpcResultBuffer); - - Debug("CopyPasteRpcInGHSetDataCB creating text: %s\n", source); - - return TRUE; - } - /* Neither file list nor text is available, send empty list back. */ - Debug("CopyPasteRpcInGHSetDataCB Send empty buf to host\n"); - return RpcIn_SetRetVals(result, resultLen, "", TRUE); - } -} - - -/* - *---------------------------------------------------------------------------- - * - * CopyPasteRpcInGHFinishCB -- - * - * For Guest->Host operations only. - * - * Invoked when host side of copyPaste operation has finished. - * - * Results: - * TRUE on success, FALSE on failure. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static Bool -CopyPasteRpcInGHFinishCB(char const **result, // OUT - size_t *resultLen, // OUT - const char *name, // IN - const char *args, // IN - size_t argsSize, // Ignored - void *clientData) // IN -{ - char *effect = NULL; - unsigned int index = 0; - - gFcpGHState.fileListNext = gFcpGHState.fileList; - - effect = StrUtil_GetNextToken(&index, args, " "); - if (!effect) { - Warning("CopyPasteRpcInGHFinishCB: no drop effect provided\n"); - return RpcIn_SetRetVals(result, resultLen, - "drop effect not provided", FALSE); - } - - Debug("CopyPasteRpcInGHFinishCB got effect %s\n", effect); - - free(effect); - return RpcIn_SetRetVals(result, resultLen, "", TRUE); -} - - -/* - *---------------------------------------------------------------------------- - * - * CopyPasteGHFileListClear -- - * - * Clears existing Guest->Host file list, releasing any used resources. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static INLINE void -CopyPasteGHFileListClear(void) -{ - Debug("CopyPasteGHFileListClear: clearing G->H file list\n"); - if (gFcpGHState.fileList) { - free(gFcpGHState.fileList); - gFcpGHState.fileList = NULL; - } - gFcpGHState.fileListSize = 0; - gFcpGHState.fileListNext = NULL; -} - - -/* - *---------------------------------------------------------------------------- - * - * CopyPasteGHFileListSet -- - * - * Sets the Guest->Host file list that is accessed through - * CopyPasteGHFileListGetNext(). - * - * Results: - * None. - * - * Side effects: - * Clears the existing Guest->Host file list if it exists. - * - *---------------------------------------------------------------------------- - */ - -static INLINE void -CopyPasteGHFileListSet(char *fileList, // IN: new Guest->Host file list - size_t fileListSize) // IN: size of the provided list -{ - CopyPasteGHFileListClear(); - gFcpGHState.fileList = fileList; - gFcpGHState.fileListSize = fileListSize; - gFcpGHState.fileListNext = fileList; - - Debug("CopyPasteGHFileListSet: [%s] (%"FMTSZ"u)\n", - CPName_Print(gFcpGHState.fileList, gFcpGHState.fileListSize), - gFcpGHState.fileListSize); -} - - -/* - *---------------------------------------------------------------------------- - * - * CopyPasteGHFileListGetNext -- - * - * Retrieves the next file in the Guest->Host file list. - * - * Note that this function may only be called after calling - * CopyPasteGHFileListSet() and before calling CopyPasteGHFileListClear(). - * - * Results: - * TRUE on success, FALSE on failure. If TRUE is returned, fileName is - * given a pointer to the filename's location or NULL if there are no more - * files, and fileNameSize is given the length of fileName. - * - * Side effects: - * The fileListNext value of the Guest->Host global state is updated. - * - *---------------------------------------------------------------------------- - */ - -Bool -CopyPasteGHFileListGetNext(char **fileName, // OUT: fill with filename location - size_t *fileNameSize) // OUT: fill with filename length -{ - char const *end; - char const *next; - int len; - - ASSERT(gFcpGHState.fileList); - ASSERT(gFcpGHState.fileListNext); - ASSERT(gFcpGHState.fileListSize > 0); - - /* Ensure end is the last NUL character */ - end = CPNameUtil_Strrchr(gFcpGHState.fileList, - gFcpGHState.fileListSize, - '\0'); - ASSERT(end); - - /* Get the length of this filename and a pointer to the next one */ - len = CPName_GetComponent(gFcpGHState.fileListNext, end, &next); - if (len < 0) { - Warning("CopyPasteGHFileListGetNext: error retrieving next component\n"); - return FALSE; - } - - /* No more entries in the list */ - if (len == 0) { - Debug("CopyPasteGHFileListGetNext: no more entries\n"); - *fileName = NULL; - *fileNameSize = 0; - gFcpGHState.fileListNext = gFcpGHState.fileList; - return TRUE; - } - - Debug("CopyPasteGHFileListGetNext: returning [%s] (%d)\n", - gFcpGHState.fileListNext, len); - - *fileName = gFcpGHState.fileListNext; - *fileNameSize = len; - gFcpGHState.fileListNext = (char *)next; - return TRUE; -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteHGSetData -- - * - * Host is sending text for copy/paste. - * - * RPC command format: - * 1. Format - * 2. Size of text - * 3. If text size > 0, then followed by text, otherwise nothing - * - * For Host->Guest operations only. - * - * Results: - * TRUE on success, FALSE otherwise. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -static Bool -CopyPasteHGSetData(char const **result, // OUT - size_t *resultLen, // OUT - const char *args) // IN -{ - char *format = NULL; - char *sSize = NULL; - uint32 textSize; - unsigned int index = 0; - Bool ret = FALSE; - int iAtom; - - /* Parse value string. */ - format = StrUtil_GetNextToken(&index, args, " "); - index++; /* Ignore leading space before data. */ - sSize = StrUtil_GetNextToken(&index, args, " "); - index++; - if (!format || !sSize) { - Debug("CopyPasteHGSetData failed to parse format & size\n"); - ret = RpcIn_SetRetVals(result, resultLen, - "format and size is not completed", FALSE); - goto exit; - } - - textSize = atoi(sSize); - gHostClipboardBuf[0] = '\0'; - - if (textSize > 0) { - if (textSize >= MAX_SELECTION_BUFFER_LENGTH) { - textSize = MAX_SELECTION_BUFFER_LENGTH - 1; - } - memcpy(gHostClipboardBuf, args + index, textSize); - gHostClipboardBuf[textSize] = '\0'; - Debug("CopyPasteHGSetData: Set text [%s]\n", gHostClipboardBuf); - } - - gtk_selection_owner_set(gUserMainWidget, - GDK_SELECTION_CLIPBOARD, - GDK_CURRENT_TIME); - gtk_selection_owner_set(gUserMainWidget, - GDK_SELECTION_PRIMARY, - GDK_CURRENT_TIME); - gIsOwner = TRUE; - gHGIsClipboardFCP = FALSE; - - /* We put text into selection, so remove file target types from list. */ - for (iAtom = 0; iAtom < NR_FCP_TARGETS; iAtom++) { - CopyPasteSelectionRemoveTarget(gUserMainWidget, - GDK_SELECTION_PRIMARY, - gFCPAtom[iAtom]); - CopyPasteSelectionRemoveTarget(gUserMainWidget, - GDK_SELECTION_CLIPBOARD, - gFCPAtom[iAtom]); - } - - ret = RpcIn_SetRetVals(result, resultLen, "", TRUE); - -exit: - free(format); - free(sSize); - return ret; -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteRpcInHGDataFinishCB -- - * - * For Host->Guest operations only. - * Host has finished transferring copyPaste data to the guest. We do any - * post H->G operation cleanup here, like picking a new file root. - * - * Results: - * TRUE on success, FALSE otherwise - * - * Side effects: - * Copied files will be deleted in error or cancel case. - * - *----------------------------------------------------------------------------- - */ - -static Bool -CopyPasteRpcInHGDataFinishCB(char const **result, // OUT - size_t *resultLen, // OUT - const char *name, // IN - const char *args, // IN - size_t argsSize, // Ignored - void *clientData) // IN: pointer to mainWnd -{ - unsigned int index = 0; - char *state; - - Debug("CopyPasteRpcInHGDataFinishCB received copypaste data finish\n"); - - state = StrUtil_GetNextToken(&index, args, " "); - - if (!state) { - Debug("CopyPasteRpcInHGDataFinishCB failed to parse data state\n"); - return RpcIn_SetRetVals(result, resultLen, - "must specify data finish state", FALSE); - } - - if (strcmp(state, "success") != 0) { - Debug("CopyPasteRpcInHGDataFinishCB data transfer error\n"); - /* - * Delete staging directory in error or cancel case, otherwise - * target application may still try to get copied files because - * the file list is provided right after adding block to staging - * directory. - */ - File_DeleteDirectoryTree(gFileRoot); - } - - free(state); - - ASSERT(gHGFCPFileTransferStatus == FCP_FILE_TRANSFERRING); - gHGFCPFileTransferStatus = FCP_FILE_TRANSFERRED; - - if (DnD_BlockIsReady(&gBlockCtrl) && - !gBlockCtrl.RemoveBlock(gBlockCtrl.fd, gFileRoot)) { - Warning("CopyPasteRpcInHGDataFinishCB: Unable to remove block [%s].\n", - gFileRoot); - } - - /* get new root dir for next FCP operation. */ - gFileRootSize = DnD_GetNewFileRoot(gFileRoot, sizeof gFileRoot); - - Debug("CopyPasteRpcInHGDataFinishCB create staging dir [%s]\n", gFileRoot); - - return RpcIn_SetRetVals(result, resultLen, "", TRUE); -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteHGSetFileList -- - * - * Host is sending file list for FCP (file copy/paste). - * - * RPC command format: - * 1. Format - * 2. Total size of all files in the list - * 3. Size of file list string - * 4. If list size > 0, then followed by file list, otherwise nothing - * - * For Host->Guest FCP operations only. - * - * Results: - * TRUE on success, FALSE on failure. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -static Bool -CopyPasteHGSetFileList(char const **result, // OUT - size_t *resultLen, // OUT - const char *args) // IN -{ - char *format = NULL; - char *data = NULL; - char *sListSize = NULL; - size_t listSize; - char *sTotalSize = NULL; - char *stagingDirName = NULL; - char mountDirName[DND_MAX_PATH]; - unsigned int index = 0; - Bool ret = FALSE; - char *retStr; - int iAtom; - Bool usingDnDBlock; - - gHGFCPFileTransferStatus = FCP_FILE_TRANSFER_NOT_YET; - /* Parse value string. */ - format = StrUtil_GetNextToken(&index, args, " "); - index++; /* Ignore leading space before data. */ - sTotalSize = StrUtil_GetNextToken(&index, args, " "); - index++; - sListSize = StrUtil_GetNextToken(&index, args, " "); - index++; - if (!format || !sTotalSize || !sListSize) { - Debug("CopyPasteHGSetFileList failed to parse format & size\n"); - retStr = "format or size is not completed"; - goto exit; - } - - listSize = atoi(sListSize); - /* - * Total file size in selection list. This is used to check if there is enough - * space in guest OS for host->guest file transfer. - */ - gHGFCPTotalSize = atol(sTotalSize); - - if (listSize <= 0) { - Debug("CopyPasteHGSetFileList: got empty list\n"); - gHostClipboardBuf[0] = '\0'; - retStr = ""; - ret = TRUE; - goto exit; - } - - /* - * XXX Should do code set convertion here from utf8 to current for file list, - * but right now should not do that. The reason is that the hgfs server - * always puts utf8 file name into guest, which is not right if local guest - * encoding is non-utf8. DnD has same problem. - */ - - data = (char *)Util_SafeCalloc(1, listSize + 1); - memcpy(data, args + index, listSize); - data[listSize] = '\0'; - - usingDnDBlock = DnD_BlockIsReady(&gBlockCtrl); - if (usingDnDBlock) { - /* - * Here we take the last component of the actual file root, which is - * a temporary directory for this DnD operation, and append it to the - * mount point for vmblock. This is where we want the target application - * to access the file since it will enable vmblock to block that - * application's progress if necessary. - */ - stagingDirName = DnD_GetLastDirName(gFileRoot); - if (!stagingDirName) { - Debug("CopyPasteHGSetFileList: error construct stagingDirName\n"); - retStr = "error construct stagingDirName"; - goto exit; - } - if (strlen(gBlockCtrl.blockRoot) + - (sizeof DIRSEPS - 1) * 2 + strlen(stagingDirName) >= sizeof mountDirName) { - Debug("CopyPasteHGSetFileList: directory name too large.\n"); - retStr = "directory name too large"; - goto exit; - } - Str_Sprintf(mountDirName, sizeof mountDirName, - "%s" DIRSEPS "%s" DIRSEPS, gBlockCtrl.blockRoot, stagingDirName); - } - - /* Add the file root to the relative paths received from host */ - if (!DnD_PrependFileRoot(usingDnDBlock ? mountDirName : gFileRoot, - &data, &listSize)) { - Debug("CopyPasteHGSetFileList: error prepending guest file root\n"); - retStr = "error prepending file root"; - goto exit; - } - - if (listSize + 1 > sizeof gHostClipboardBuf) { - Debug("CopyPasteHGSetFileList: data too large\n"); - retStr = "data too large"; - goto exit; - } - - memcpy(gHostClipboardBuf, data, listSize + 1); - gGHFCPListSize = listSize; - gHGIsClipboardFCP = TRUE; - Debug("CopyPasteHGSetFileList: get file list [%s] (%zu)\n", - CPName_Print(gHostClipboardBuf, gGHFCPListSize), gGHFCPListSize); - - for (iAtom = 0; iAtom < NR_FCP_TARGETS; iAtom++) { - gtk_selection_add_target(gUserMainWidget, GDK_SELECTION_PRIMARY, - gFCPAtom[iAtom], 0); - gtk_selection_add_target(gUserMainWidget, GDK_SELECTION_CLIPBOARD, - gFCPAtom[iAtom], 0); - } - - Debug("CopyPasteHGSetFileList: added targets\n"); - gtk_selection_owner_set(gUserMainWidget, - GDK_SELECTION_CLIPBOARD, - GDK_CURRENT_TIME); - gtk_selection_owner_set(gUserMainWidget, - GDK_SELECTION_PRIMARY, - GDK_CURRENT_TIME); - gIsOwner = TRUE; - - retStr = ""; - ret = TRUE; - -exit: - free(format); - free(data); - free(sTotalSize); - free(sListSize); - free(stagingDirName); - Hostinfo_GetTimeOfDay(&gHGGetListTime); - return RpcIn_SetRetVals(result, resultLen, retStr, ret); -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteRpcInHGSetDataCB -- - * - * Host is sending data for copy/paste. The data can be text, file list, etc. - * - * For Host->Guest operations only. - * - * Results: - * TRUE if success, FALSE otherwise. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -static Bool -CopyPasteRpcInHGSetDataCB(char const **result, // OUT - size_t *resultLen, // OUT - const char *name, // IN - const char *args, // IN - size_t argsSize, // Ignored - void *clientData) // IN: ignored -{ - char *formatStr = NULL; - DND_CPFORMAT format; - Bool ret = FALSE; - - unsigned int index = 0; - - if (gHGFCPFileTransferStatus == FCP_FILE_TRANSFERRING) { - return RpcIn_SetRetVals(result, resultLen, - "", TRUE); - } - - /* Parse value string. */ - formatStr = StrUtil_GetNextToken(&index, args, " "); - index++; /* Ignore leading space before data. */ - - if (!formatStr) { - Debug("CopyPasteTcloHGDataSet failed to parse format\n"); - return RpcIn_SetRetVals(result, resultLen, - "format and size is not completed", FALSE); - } - - format = (DND_CPFORMAT)atoi(formatStr); - free(formatStr); - - switch (format) { - case CPFORMAT_TEXT: - ret = CopyPasteHGSetData(result, resultLen, args); - break; - case CPFORMAT_FILELIST: - /* Only vmx version greater than 2 support file copy/paste. */ - if (gVmxCopyPasteVersion < 2) { - Debug("CopyPasteRpcInHGSetDataCB invalid operation\n"); - return RpcIn_SetRetVals(result, resultLen, - "invalid operation", FALSE); - } - ret = CopyPasteHGSetFileList(result, resultLen, args); - break; - default: - Debug("CopyPasteTcloHGDataSet unknown format\n"); - ret = RpcIn_SetRetVals(result, resultLen, - "unknown format", FALSE); - break; - } - - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * CopyPasteRpcInGHGetNextFileCB -- - * - * For Guest->Host operations only. - * - * Invoked when the host is compiling its list of files to copy from the - * guest. Here we provide the path of the next file in our Guest->Host file - * list in guest path format (for display purposes) and CPName format (for - * file copy operation). - * - * Results: - * TRUE on success, FALSE on failure. - * - * Side effects: - * Iterator pointer within file list of GH state is iterated to next list - * entry (through call to CopyPasteGHFileListGetNext()). - * - *---------------------------------------------------------------------------- - */ - -static Bool -CopyPasteRpcInGHGetNextFileCB(char const **result, // OUT - size_t *resultLen, // OUT - const char *name, // IN - const char *args, // IN - size_t argsSize, // Ignored - void *clientData) // IN -{ - static char resultBuffer[DND_MAX_PATH]; - char *fileName; - size_t fileNameSize; - uint32 cpNameSize; - Bool res; - - /* - * Retrieve a pointer to the next filename and its size from the list stored - * in the G->H DnD state. Note that fileName should not be free(3)d here - * since an additional copy is not allocated. - */ - res = CopyPasteGHFileListGetNext(&fileName, &fileNameSize); - - if (!res) { - Warning("CopyPasteRpcInGHGetNextFileCB: error retrieving file name\n"); - return RpcIn_SetRetVals(result, resultLen, "error getting file", FALSE); - } - - if (!fileName) { - /* There are no more files to send */ - Debug("CopyPasteRpcInGHGetNextFileCB: reached end of Guest->Host file list\n"); - return RpcIn_SetRetVals(result, resultLen, "|end|", TRUE); - } - - if (fileNameSize + 1 + fileNameSize > sizeof resultBuffer) { - Warning("CopyPasteRpcInGHGetNextFileCB: filename too large (%"FMTSZ"u)\n", fileNameSize); - return RpcIn_SetRetVals(result, resultLen, "filename too large", FALSE); - } - - /* - * Construct a reply message of the form: - * - */ - memcpy(resultBuffer, fileName, fileNameSize); - resultBuffer[fileNameSize] = '\0'; - - cpNameSize = CPNameUtil_ConvertToRoot(fileName, - sizeof resultBuffer - (fileNameSize + 1), - resultBuffer + fileNameSize + 1); - if (cpNameSize < 0) { - Warning("CopyPasteRpcInGHGetNextFileCB: could not convert to CPName\n"); - return RpcIn_SetRetVals(result, resultLen, - "error on CPName conversion", FALSE); - } - - /* Set manually because RpcIn_SetRetVals() assumes no NUL characters */ - *result = resultBuffer; - *resultLen = fileNameSize + 1 + cpNameSize; - - Debug("CopyPasteRpcInGHGetNextFileCB: [%s] (%"FMTSZ"u)\n", - CPName_Print(*result, *resultLen), *resultLen); - - return TRUE; -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPaste_GetVmxCopyPasteVersion -- - * - * Ask the vmx for it's copy/paste version. - * - * Results: - * The copy/paste version the vmx supports, 1 if the vmx doesn't know - * what we're talking about. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -int32 -CopyPaste_GetVmxCopyPasteVersion(void) -{ - char *reply = NULL; - size_t replyLen; - - Debug("%s: enter\n", __FUNCTION__); - if (!RpcOut_sendOne(&reply, &replyLen, "vmx.capability.copypaste_version")) { - Debug("CopyPaste_GetVmxCopyPasteVersion: could not get VMX copyPaste " - "version capability: %s\n", reply ? reply : "NULL"); - gVmxCopyPasteVersion = 1; - } else { - gVmxCopyPasteVersion = atoi(reply); - } - - free(reply); - Debug("CopyPaste_GetVmxCopyPasteVersion: got version %d\n", gVmxCopyPasteVersion); - return gVmxCopyPasteVersion; -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPaste_RegisterCapability -- - * - * Register the "copypaste" capability. Sometimes this needs to be done - * separately from the rest of copy/paste registration, so we provide it - * separately here. - * - * Results: - * TRUE on success - * FALSE on failure - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -CopyPaste_RegisterCapability(void) -{ - Debug("%s: enter\n", __FUNCTION__); - /* Tell the VMX about the copyPaste version we support. */ - if (!RpcOut_sendOne(NULL, NULL, "tools.capability.copypaste_version 2")) { - Debug("CopyPaste_RegisterCapability: could not set guest copypaste " - "version capability\n"); - gVmxCopyPasteVersion = 1; - return FALSE; - } - Debug("CopyPaste_RegisterCapability: set copypaste version 2\n"); - return TRUE; -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPaste_Register -- - * - * Setup callbacks, initialize. - * - * Results: - * Always TRUE. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -CopyPaste_Register(GtkWidget* mainWnd) -{ - Debug("%s: enter\n", __FUNCTION__); - /* Text copy/paste initialization for all versions. */ -#ifndef GDK_SELECTION_CLIPBOARD - GDK_SELECTION_CLIPBOARD = gdk_atom_intern("CLIPBOARD", FALSE); -#endif - -#ifndef GDK_SELECTION_TYPE_TIMESTAMP - GDK_SELECTION_TYPE_TIMESTAMP = gdk_atom_intern("TIMESTAMP", FALSE); -#endif - -#ifndef GDK_SELECTION_TYPE_UTF8_STRING - GDK_SELECTION_TYPE_UTF8_STRING = gdk_atom_intern("UTF8_STRING", FALSE); -#endif - - gFCPAtom[FCP_TARGET_INFO_GNOME_COPIED_FILES] = - gdk_atom_intern(FCP_TARGET_NAME_GNOME_COPIED_FILES, FALSE); - gFCPAtom[FCP_TARGET_INFO_URI_LIST] = - gdk_atom_intern(FCP_TARGET_NAME_URI_LIST, FALSE); - - /* - * String is always in supported list. FCP atoms will dynamically be - * added and removed. - */ - gtk_selection_add_target(mainWnd, GDK_SELECTION_PRIMARY, - GDK_SELECTION_TYPE_STRING, 0); - gtk_selection_add_target(mainWnd, GDK_SELECTION_CLIPBOARD, - GDK_SELECTION_TYPE_STRING, 0); - gtk_selection_add_target(mainWnd, GDK_SELECTION_PRIMARY, - GDK_SELECTION_TYPE_UTF8_STRING, 0); - gtk_selection_add_target(mainWnd, GDK_SELECTION_CLIPBOARD, - GDK_SELECTION_TYPE_UTF8_STRING, 0); - - gtk_signal_connect(GTK_OBJECT(mainWnd), "selection_received", - GTK_SIGNAL_FUNC(CopyPasteSelectionReceivedCB), mainWnd); - gtk_signal_connect(GTK_OBJECT(mainWnd), "selection_get", - GTK_SIGNAL_FUNC(CopyPasteSelectionGetCB), mainWnd); - gtk_signal_connect(GTK_OBJECT(mainWnd), "selection_clear_event", - GTK_SIGNAL_FUNC(CopyPasteSelectionClearCB), mainWnd); - - RpcIn_RegisterCallback(gRpcIn, "copypaste.hg.data.set", - CopyPasteRpcInHGSetDataCB, NULL); - RpcIn_RegisterCallback(gRpcIn, "copypaste.hg.data.finish", - CopyPasteRpcInHGDataFinishCB, NULL); - RpcIn_RegisterCallback(gRpcIn, "copypaste.gh.data.get", - CopyPasteRpcInGHSetDataCB, NULL); - RpcIn_RegisterCallback(gRpcIn, "copypaste.gh.get.next.file", - CopyPasteRpcInGHGetNextFileCB, NULL); - RpcIn_RegisterCallback(gRpcIn, "copypaste.gh.finish", - CopyPasteRpcInGHFinishCB, NULL); - - CopyPasteStateInit(); - Wiper_Init(NULL); - - return CopyPaste_RegisterCapability(); -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPaste_Unregister -- - * - * Cleanup copy/paste related things. - * - * Results: - * None. - * - * Side effects: - * copy/paste is stopped, the rpc channel to the vmx is closed. - * - *----------------------------------------------------------------------------- - */ - -void -CopyPaste_Unregister(GtkWidget* mainWnd) -{ - Debug("%s: enter\n", __FUNCTION__); - gtk_signal_disconnect_by_func(GTK_OBJECT(mainWnd), - GTK_SIGNAL_FUNC(CopyPasteSelectionReceivedCB), - mainWnd); - gtk_signal_disconnect_by_func(GTK_OBJECT(mainWnd), - GTK_SIGNAL_FUNC(CopyPasteSelectionGetCB), - mainWnd); - gtk_signal_disconnect_by_func(GTK_OBJECT(mainWnd), - GTK_SIGNAL_FUNC(CopyPasteSelectionClearCB), - mainWnd); - RpcIn_UnregisterCallback(gRpcIn, "copypaste.hg.data.set"); - RpcIn_UnregisterCallback(gRpcIn, "copypaste.hg.data.finish"); - RpcIn_UnregisterCallback(gRpcIn, "copypaste.gh.data.get"); - RpcIn_UnregisterCallback(gRpcIn, "copypaste.gh.get.next.file"); - RpcIn_UnregisterCallback(gRpcIn, "copypaste.gh.finish"); -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPaste_OnReset -- - * - * Handles reinitializing Copy Paste state on a reset. - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -void -CopyPaste_OnReset(void) -{ - Debug("%s: enter\n", __FUNCTION__); - if (gHGFCPFileTransferStatus == FCP_FILE_TRANSFERRING) { - File_DeleteDirectoryTree(gFileRoot); - if (DnD_BlockIsReady(&gBlockCtrl) && - !gBlockCtrl.RemoveBlock(gBlockCtrl.fd, gFileRoot)) { - Warning("CopyPasteRpcInHGDataFinishCB: Unable to remove block [%s].\n", - gFileRoot); - } - gFileRootSize = DnD_GetNewFileRoot(gFileRoot, sizeof gFileRoot); - } - - CopyPasteStateInit(); -} - -/* - *---------------------------------------------------------------------------- - * - * CopyPaste_InProgress -- - * - * Indicates whether a copy/paste data transfer is currently in progress. - * - * Results: - * TRUE if in progress, FALSE otherwise. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -Bool -CopyPaste_InProgress(void) -{ - /* XXX We currently have no way to determine if a G->H FCP is ongoing. */ - return gHGFCPFileTransferStatus == FCP_FILE_TRANSFERRING; -} - - -/* - *---------------------------------------------------------------------------- - * - * CopyPaste_IsRpcCPSupported -- - * - * Check if RPC copy/paste is supported by vmx or not. - * - * Results: - * TRUE if RPC copy/paste is supported, FALSE otherwise. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -Bool -CopyPaste_IsRpcCPSupported(void) -{ - return gVmxCopyPasteVersion > 1; -} - - -/* - *---------------------------------------------------------------------------- - * - * CopyPasteStateInit -- - * - * Initalialize CopyPaste State. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -void -CopyPasteStateInit(void) -{ - Debug("%s: enter\n", __FUNCTION__); - gHostClipboardBuf[0] = '\0'; - gGuestSelPrimaryBuf[0] = '\0'; - gGuestSelClipboardBuf[0] = '\0'; - gIsOwner = FALSE; - gGHFCPRpcResultBuffer = NULL; - gHGFCPPending = FALSE; - gHGFCPFileTransferStatus = FCP_FILE_TRANSFER_NOT_YET; - - if (CopyPaste_GetVmxCopyPasteVersion() >= 2) { - /* - * Create staging directory for file copy/paste. This is for vmx with version 2 - * or greater. - */ - gFileRootSize = DnD_GetNewFileRoot(gFileRoot, sizeof gFileRoot); - Debug("%s: create file root [%s]\n", __FUNCTION__, gFileRoot); - } -} diff --git a/open-vm-tools/vmware-user/copyPasteDnDWrapper.cpp b/open-vm-tools/vmware-user/copyPasteDnDWrapper.cpp deleted file mode 100644 index 65d64f8fd..000000000 --- a/open-vm-tools/vmware-user/copyPasteDnDWrapper.cpp +++ /dev/null @@ -1,503 +0,0 @@ -/********************************************************* - * Copyright (C) 2009 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - -/** - * @file copyPasteDnDWrapper.cpp - * - * This singleton class implements a wrapper around various versions of - * copy and paste and dnd protocols, and provides some convenience functions - * that help to make VMwareUser a bit cleaner. - */ - -#include "copyPasteDnDWrapper.h" - -extern "C" { -#include "vmwareuserInt.h" -#include "debug.h" -#include "dndGuest.h" -#include "unity.h" -} - -class DragDetWnd; - -/** - * CopyPasteDnDWrapper is a singleton, here is a pointer to its only instance. - */ -CopyPasteDnDWrapper *CopyPasteDnDWrapper::m_instance = 0; - -/** - * - * Get an instance of CopyPasteDnDWrapper, which is an application singleton. - * - * @return a pointer to the singleton CopyPasteDnDWrapper object, or NULL if - * for some reason it could not be allocated. - */ - -CopyPasteDnDWrapper * -CopyPasteDnDWrapper::GetInstance() -{ - if (!m_instance) { - m_instance = new CopyPasteDnDWrapper; - } - return m_instance; -} - -#if defined(HAVE_GTKMM) -extern "C" { - -/** - * - * Enter or leave unity mode. - * - * @param[in] mode enter unity mode if TRUE, else leave. - */ - -void -CopyPasteDnDWrapper_SetUnityMode(Bool mode) -{ - CopyPasteDnDWrapper *wrapper = CopyPasteDnDWrapper::GetInstance(); - - if (wrapper) { - wrapper->SetUnityMode(mode); - } -} - -} -#endif - -/** - * - * Constructor. - */ - -CopyPasteDnDWrapper::CopyPasteDnDWrapper() : -#if defined(HAVE_GTKMM) - m_copyPasteUI(NULL), - m_dndUI(NULL), -#endif - m_isCPRegistered(FALSE), - m_isDnDRegistered(FALSE), - m_userData(NULL), - m_cpVersion(-1), - m_dndVersion(-1), - m_isLegacy(false), - m_hgWnd(NULL), - m_ghWnd(NULL), - m_eventQueue(NULL) -{ -} - - -/** - * - * Destructor. - */ - -CopyPasteDnDWrapper::~CopyPasteDnDWrapper() -{ - if (IsCPRegistered()) { - UnregisterCP(); - } - if (IsDnDRegistered()) { - UnregisterDnD(); - } -} - - -/** - * - * Attach implementation-specific data (in reality, a GtkWidget * that is - * needed by the legacy copy paste code. Going forward, any new protocol - * versions should be implemented as classes, and should not need such a - * crutch). - * - * @param[in] userData a GtkWidget created by VMwareUser and used by the - * legacy copy and paste implementation. - */ - -void -CopyPasteDnDWrapper::SetUserData(const void *userData) -{ - Debug("%s: enter %lx\n", __FUNCTION__, (unsigned long) userData); - m_userData = userData; -} - - -/** - * - * Set block fd. - * - * @param[in] blockFd blockFd specified as command line arg by VMwareUser. - */ - -void -CopyPasteDnDWrapper::SetBlockControl(DnDBlockControl *blockCtrl) -{ - Debug("%s: enter %p (%d)\n", __func__, blockCtrl, blockCtrl->fd); - m_blockCtrl = blockCtrl; -} - - -/** - * - * Register copy and paste capabilities with the VMX. Try newest version - * first, then fall back to the legacy implementation. - * - * @return TRUE on success, FALSE on failure - */ - -bool -CopyPasteDnDWrapper::RegisterCP() -{ - Debug("%s: m_blockCtrl %p\n", __func__, m_blockCtrl); - if (IsCPRegistered()) { - return TRUE; - } - - /* - * Try to get version 3, and if that fails, go for the compatibility - * versions (1 and 2). - */ - -#if defined(HAVE_GTKMM) - if (!IsCPRegistered()) { - m_copyPasteUI = new CopyPasteUI(); - if (m_copyPasteUI) { - Debug("%s: Setting block control to %p (fd %d)\n", - __func__, m_blockCtrl, m_blockCtrl->fd); - m_copyPasteUI->SetBlockControl(m_blockCtrl); - if (m_copyPasteUI->Init()) { - SetCPIsRegistered(TRUE); - int version = GetCPVersion(); - Debug("%s: version is %d\n", __FUNCTION__, version); - if (version >= 3) { - m_copyPasteUI->VmxCopyPasteVersionChanged(gRpcIn, version); - m_copyPasteUI->SetCopyPasteAllowed(TRUE); - m_isLegacy = false; - } else { - Debug("%s: version < 3, unregistering.\n", __FUNCTION__); - UnregisterCP(); - } - } else { - delete m_copyPasteUI; - m_copyPasteUI = NULL; - } - } - } - -#endif - if (!IsCPRegistered()) { - Debug("%s: Registering legacy m_userData %lx\n", - __func__, (long unsigned int) m_userData); - SetCPIsRegistered(CopyPaste_Register((GtkWidget *)m_userData)); - if (IsCPRegistered()) { - Debug("%s: Registering capability\n", __FUNCTION__); - if (!CopyPaste_RegisterCapability()) { - UnregisterCP(); - } - else { - m_isLegacy = true; - } - } - } - - return IsCPRegistered(); -} - - -/** - * - * Cancel DnD and copy paste. - */ - -void -CopyPasteDnDWrapper::Cancel() -{ -#if defined(HAVE_GTKMM) - if (m_dndUI) { - m_dndUI->Cancel(); - } - if (m_copyPasteUI) { - m_copyPasteUI->Cancel(); - } -#endif -} - - -/** - * - * Register DnD capabilities with the VMX. Try newest version - * first, then fall back to the legacy implementation. - * - * @return TRUE on success, FALSE on failure - */ - -bool -CopyPasteDnDWrapper::RegisterDnD() -{ - /* - * Try to get version 3, and if that fails, go for the compatibility - * versions (1 and 2). - */ - -#if defined(HAVE_GTKMM) - if (!IsDnDRegistered()) { - m_dndUI = new DnDUI(m_eventQueue); - if (m_dndUI) { - Debug("%s: Setting block control to %p (fd %d)\n", - __func__, m_blockCtrl, m_blockCtrl->fd); - m_dndUI->SetBlockControl(m_blockCtrl); - - if (m_dndUI->Init()) { - UnityDnD state; - state.detWnd = m_dndUI->GetDetWndAsWidget(); - state.setMode = CopyPasteDnDWrapper_SetUnityMode; - Unity_SetActiveDnDDetWnd(&state); - - SetDnDIsRegistered(TRUE); - int version = GetDnDVersion(); - Debug("%s: dnd version is %d\n", __FUNCTION__, version); - if (version >= 3) { - Debug("%s: calling VmxDnDVersionChanged (version %d) and SetDnDAllowed\n", - __FUNCTION__, version); - m_dndUI->VmxDnDVersionChanged(gRpcIn, version); - m_dndUI->SetDnDAllowed(TRUE); - m_isLegacy = false; - } else { - Debug("%s: version < 3, unregistering.\n", __FUNCTION__); - UnregisterDnD(); - } - } else { - delete m_dndUI; - m_dndUI = NULL; - } - } - } - -#endif - if (!IsDnDRegistered()) { - Debug("%s: legacy registering dnd capability\n", __FUNCTION__); - if (m_isLegacy) { - SetDnDIsRegistered(DnD_Register(m_hgWnd, m_ghWnd)); - if (IsDnDRegistered()) { - Debug("%s: setting up detwnd for Unity\n", __FUNCTION__); - UnityDnD state; - state.detWnd = m_ghWnd; - state.setMode = DnD_SetMode; - Unity_SetActiveDnDDetWnd(&state); - } - } - } else if (m_isLegacy && DnD_GetVmxDnDVersion() > 1) { - Debug("%s: legacy registering dnd capability\n", __FUNCTION__); - if (!DnD_RegisterCapability()) { - Debug("%s: legacy unable to register dnd capability\n", __FUNCTION__); - UnregisterDnD(); - } - } - Debug("%s: dnd is registered? %d\n", __FUNCTION__, (int) IsDnDRegistered()); - return IsDnDRegistered(); -} - - -/** - * - * Unregister copy paste capabilities and do general cleanup. - */ - -void -CopyPasteDnDWrapper::UnregisterCP() -{ - Debug("%s: enter\n", __FUNCTION__); - if (IsCPRegistered()) { -#if defined(HAVE_GTKMM) - if (m_copyPasteUI) { - delete m_copyPasteUI; - m_copyPasteUI = NULL; - } else { -#endif - CopyPaste_Unregister((GtkWidget *)m_userData); -#if defined(HAVE_GTKMM) - } -#endif - SetCPIsRegistered(FALSE); - m_cpVersion = -1; - } -} - - -/** - * - * Unregister DnD capabilities and do general cleanup. - */ - -void -CopyPasteDnDWrapper::UnregisterDnD() -{ - Debug("%s: enter\n", __FUNCTION__); - if (IsDnDRegistered()) { - /* - * Detach the DnD detection window from Unity. - */ - UnityDnD state = { NULL, NULL }; - Unity_SetActiveDnDDetWnd(&state); - - if (m_isLegacy) { - DnD_Unregister(m_hgWnd, m_ghWnd); -#if defined(HAVE_GTKMM) - } else if (m_dndUI) { - delete m_dndUI; - m_dndUI = NULL; -#endif - } - m_dndVersion = -1; - SetDnDIsRegistered(false); - return; - } -} - - -/** - * - * Get the version of the copy paste protocol being wrapped. - * - * @return copy paste protocol version. - */ - -int -CopyPasteDnDWrapper::GetCPVersion() -{ - if (IsCPRegistered()) { - m_cpVersion = CopyPaste_GetVmxCopyPasteVersion(); - } - Debug("%s: got version %d\n", __FUNCTION__, m_cpVersion); - return m_cpVersion; -} - - -/** - * - * Get the version of the DnD protocol being wrapped. - * - * @return DnD protocol version. - */ - -int -CopyPasteDnDWrapper::GetDnDVersion() -{ - if (IsDnDRegistered()) { - m_dndVersion = DnD_GetVmxDnDVersion(); - } - Debug("%s: got version %d\n", __FUNCTION__, m_dndVersion); - return m_dndVersion; -} - - -/** - * - * Set a flag indicating that we are wrapping an initialized and registered - * copy paste implementation, or not. - * - * @param[in] isRegistered If TRUE, protocol is registered, otherwise FALSE. - */ - -void -CopyPasteDnDWrapper::SetCPIsRegistered(bool isRegistered) -{ - m_isCPRegistered = isRegistered; -} - - -/** - * - * Get the flag indicating that we are wrapping an initialized and registered - * copy paste implementation, or not. - * - * @return TRUE if copy paste is initialized, otherwise FALSE. - */ - -bool -CopyPasteDnDWrapper::IsCPRegistered() -{ - return m_isCPRegistered; -} - - -/** - * - * Set a flag indicating that we are wrapping an initialized and registered - * DnD implementation, or not. - * - * @param[in] isRegistered If TRUE, protocol is registered, otherwise FALSE. - */ - -void -CopyPasteDnDWrapper::SetDnDIsRegistered(bool isRegistered) -{ - m_isDnDRegistered = isRegistered; -} - - -/** - * - * Get the flag indicating that we are wrapping an initialized and registered - * DnD implementation, or not. - * - * @return TRUE if DnD is initialized, otherwise FALSE. - */ - -bool -CopyPasteDnDWrapper::IsDnDRegistered() -{ - return m_isDnDRegistered; -} - - -/** - * - * Handle reset by calling protocol dependent handlers. - */ - -void -CopyPasteDnDWrapper::OnReset() -{ - Debug("%s: enter\n", __FUNCTION__); - if (IsDnDRegistered()) { - UnregisterDnD(); - } - if (IsCPRegistered()) { - UnregisterCP(); - } - if (!IsCPRegistered()) { - RegisterCP(); - } - if (!IsDnDRegistered()) { - RegisterDnD(); - } - if (!IsDnDRegistered() || !IsCPRegistered()) { - Debug("%s: unable to reset fully!\n", __FUNCTION__); - } - if (m_isLegacy) { - if (IsCPRegistered()) { - CopyPaste_OnReset(); - } - if (IsDnDRegistered()) { - DnD_OnReset(m_hgWnd, m_ghWnd); - } - } -} - diff --git a/open-vm-tools/vmware-user/copyPasteDnDWrapper.h b/open-vm-tools/vmware-user/copyPasteDnDWrapper.h deleted file mode 100644 index 474c67251..000000000 --- a/open-vm-tools/vmware-user/copyPasteDnDWrapper.h +++ /dev/null @@ -1,99 +0,0 @@ -/********************************************************* - * Copyright (C) 2009 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - -/** - * @file copyPasteDnDWrapper.h - * - * This singleton class implements a wrapper around various versions of - * copy and paste and dnd versions for Linux. - * - */ - -#ifndef COPYPASTEDNDWRAPPER_H -#define COPYPASTEDNDWRAPPER_H - -extern "C" { -#include "dnd.h" /* for DnDBlockControl */ -} - -#if defined(HAVE_GTKMM) -#include "copyPasteUI.h" -#include "dndUI.h" -#endif - -#include "vm_basic_types.h" -#include - -struct DblLnkLst_Links; - -extern "C" { -void CopyPasteDnDWrapper_SetUnityMode(Bool mode); -} - -class CopyPasteDnDWrapper -{ -public: - ~CopyPasteDnDWrapper(); - static CopyPasteDnDWrapper *GetInstance(); - bool RegisterCP(); - void UnregisterCP(); - bool RegisterDnD(); - void UnregisterDnD(); - int GetCPVersion(); - int GetDnDVersion(); - void SetCPIsRegistered(bool isRegistered); - bool IsCPRegistered(); - void SetDnDIsRegistered(bool isRegistered); - bool IsDnDRegistered(); - void OnReset(); - void Cancel(); - void SetBlockControl(DnDBlockControl *blockCtrl); - void SetUserData(const void *userData); - void SetHGWnd(GtkWidget *wnd) {m_hgWnd = wnd;}; - void SetGHWnd(GtkWidget *wnd) {m_ghWnd = wnd;}; - void SetEventQueue(DblLnkLst_Links *queue) {m_eventQueue = queue;}; -#if defined(HAVE_GTKMM) - void SetUnityMode(Bool mode) - {m_dndUI->SetUnityMode(mode);}; -#endif -private: - /* - * We're a singleton, so it is a compile time error to call these. - */ - CopyPasteDnDWrapper(); - CopyPasteDnDWrapper(const CopyPasteDnDWrapper &wrapper); - CopyPasteDnDWrapper& operator=(const CopyPasteDnDWrapper &wrapper); -private: -#if defined(HAVE_GTKMM) - CopyPasteUI *m_copyPasteUI; - DnDUI *m_dndUI; -#endif - bool m_isCPRegistered; - bool m_isDnDRegistered; - const void *m_userData; - int m_cpVersion; - int m_dndVersion; - static CopyPasteDnDWrapper *m_instance; - DnDBlockControl *m_blockCtrl; - bool m_isLegacy; - GtkWidget *m_hgWnd; - GtkWidget *m_ghWnd; - DblLnkLst_Links *m_eventQueue; -}; - -#endif // COPYPASTEDNDWRAPPER_H diff --git a/open-vm-tools/vmware-user/copyPasteUI.cpp b/open-vm-tools/vmware-user/copyPasteUI.cpp deleted file mode 100644 index 46f923625..000000000 --- a/open-vm-tools/vmware-user/copyPasteUI.cpp +++ /dev/null @@ -1,1426 +0,0 @@ -/********************************************************* - * Copyright (C) 2009 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - -/* - * copyPasteUI.cpp -- - * - * This class implements the methods that allows CopyPaste between host - * and guest. - * - * For a perspective on X copy/paste, see - * http://www.jwz.org/doc/x-cut-and-paste.html - */ - -#include -#include -#include "copyPasteUI.h" -#include "dndFileList.hh" - -extern "C" { - #include "vmwareuserInt.h" - #include "vmblock.h" - #include "file.h" - #include "dnd.h" - #include "dndMsg.h" - #include "dndClipboard.h" - #include "cpName.h" - #include "debug.h" - #include "cpNameUtil.h" - #include "rpcout.h" - #include "eventManager.h" - #include "vmware/guestrpc/tclodefs.h" -} - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteUI::CopyPasteUI -- - * - * Constructor. - * - * Results: - * None - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -CopyPasteUI::CopyPasteUI() - : mClipboardEmpty(true), - mHGStagingDir(""), - mIsClipboardOwner(false), - mHGGetFilesInitiated(false), - mFileTransferDone(false), - mBlockAdded(false), - mBlockCtrl(0), - mInited(false) -{ -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteUI::Init -- - * - * Initialize copy paste UI class and register for V3 or greater copy - * paste. - * - * Results: - * None - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -bool -CopyPasteUI::Init() -{ - if (mInited) { - return true; - } - - CPClipboard_Init(&mClipboard); - - Gtk::TargetEntry gnome(FCP_TARGET_NAME_GNOME_COPIED_FILES); - Gtk::TargetEntry kde(FCP_TARGET_NAME_URI_LIST); - gnome.set_info(FCP_TARGET_INFO_GNOME_COPIED_FILES); - kde.set_info(FCP_TARGET_INFO_URI_LIST); - - mListTargets.push_back(gnome); - mListTargets.push_back(kde); - - /* Tell the VMX about the copyPaste version we support. */ - if (!RpcOut_sendOne(NULL, NULL, "tools.capability.copypaste_version 3")) { - Debug("%s: could not set guest copypaste version capability\n", - __FUNCTION__); - return false; - } - Debug("%s: set copypaste version 3\n", __FUNCTION__); - - mCP.newClipboard.connect( - sigc::mem_fun(this, &CopyPasteUI::GetRemoteClipboardCB)); - mCP.localGetClipboard.connect( - sigc::mem_fun(this, &CopyPasteUI::GetLocalClipboard)); - mCP.localGetFilesDoneChanged.connect( - sigc::mem_fun(this, &CopyPasteUI::GetLocalFilesDone)); - mInited = true; - return true; -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteUI::~CopyPaste -- - * - * Destructor. - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -CopyPasteUI::~CopyPasteUI() -{ - CPClipboard_Destroy(&mClipboard); -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteUI::Cancel -- - * - * Cancel file transfer and remove block. - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -void -CopyPasteUI::Cancel() -{ - Debug("%s: enter\n", __FUNCTION__); - if (mBlockAdded) { - DnD_DeleteStagingFiles(mHGStagingDir.c_str(), FALSE); - Debug("%s: removing block for %s\n", __FUNCTION__, mHGStagingDir.c_str()); - mBlockCtrl->RemoveBlock(mBlockCtrl->fd, mHGStagingDir.c_str()); - mBlockAdded = false; - } - - mFileTransferDone = true; -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteUI::VmxCopyPasteVersionChanged -- - * - * Update version information in mCP. - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -void -CopyPasteUI::VmxCopyPasteVersionChanged(struct RpcIn *rpcIn, // IN - uint32 version) // IN -{ - Debug("%s: new version is %d\n", __FUNCTION__, version); - mCP.VmxCopyPasteVersionChanged(rpcIn, version); -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteUI::GetLocalClipboard -- - * - * Retrives the data from local clipboard and sends it to host. Send empty - * data back if there is no data or can not get data successfully. For - * guest->host copy/paste. - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -bool -CopyPasteUI::GetLocalClipboard(CPClipboard *clip) // OUT -{ - Debug("%s: enter.\n", __FUNCTION__); - - if (mIsClipboardOwner) { - Debug("%s: is clipboard owner, set changed to false and return.\n", __FUNCTION__); - CPClipboard_SetChanged(clip, FALSE); - return true; - } - - if (!mCP.IsCopyPasteAllowed()) { - Debug("%s: copyPaste is not allowed\n", __FUNCTION__); - return true; - } - - Glib::RefPtr refClipboard = - Gtk::Clipboard::get(GDK_SELECTION_CLIPBOARD); - - mClipTime = 0; - mPrimTime = 0; - mGHSelection = GDK_SELECTION_CLIPBOARD; - Debug("%s: retrieving timestamps\n", __FUNCTION__); - refClipboard->request_contents(TARGET_NAME_TIMESTAMP, - sigc::mem_fun(this, &CopyPasteUI::LocalClipboardTimestampCB)); - return false; -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteUI::GetCurrentTime -- - * - * Get current time in microseconds. - * - * Results: - * Time in microseconds. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -VmTimeType -CopyPasteUI::GetCurrentTime(void) -{ - struct timeval tv; - VmTimeType curTime; - - if (gettimeofday(&tv, NULL) != 0) { - Debug("%s: gettimeofday failed!\n", __FUNCTION__); - return (VmTimeType) 0; - } - curTime = (tv.tv_sec * 1000000 + tv.tv_usec); - return curTime; -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteUI::LocalGetFileRequestCB -- - * - * Callback from a file paste request from another guest application. - * Begins copying the files from host to guest and return the file list. - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -void -CopyPasteUI::LocalGetFileRequestCB(Gtk::SelectionData& sd, // IN: - guint info) // IN: -{ - Debug("%s: enter.\n", __FUNCTION__); - mHGCopiedUriList = ""; - VmTimeType curTime; - mBlockAdded = false; - - sd.set(sd.get_target().c_str(), ""); - - curTime = GetCurrentTime(); - - /* - * Some applications may ask for clipboard contents right after clipboard - * owner changed. So HG FCP will return nothing for some time after switch - * from guest OS to host OS. - */ - if ((curTime - mHGGetListTime) < FCP_COPY_DELAY) { - Debug("%s: time delta less than FCP_COPY_DELAY, returning.\n", - __FUNCTION__); - return; - } - - if (!mIsClipboardOwner || !mCP.IsCopyPasteAllowed()) { - Debug("%s: not clipboard ownder, or copy paste not allowed, returning.\n", - __FUNCTION__); - return; - } - - Debug("%s: Got paste request, target is %s\n", __FUNCTION__, - sd.get_target().c_str()); - - /* Copy the files. */ - if (!mHGGetFilesInitiated) { - utf::string str; - utf::string hgStagingDir; - utf::string stagingDirName; - utf::string pre; - utf::string post; - size_t index = 0; - mFileTransferDone = false; - - hgStagingDir = static_cast(mCP.GetFiles()); - Debug("%s: Getting files. Staging dir: %s", __FUNCTION__, - hgStagingDir.c_str()); - - if (0 == hgStagingDir.bytes()) { - Debug("%s: Can not create staging directory\n", __FUNCTION__); - return; - } - mHGGetFilesInitiated = true; - - if (DnD_BlockIsReady(mBlockCtrl) && mBlockCtrl->AddBlock(mBlockCtrl->fd, hgStagingDir.c_str())) { - Debug("%s: add block for %s.\n", - __FUNCTION__, hgStagingDir.c_str()); - mBlockAdded = true; - } else { - Debug("%s: unable to add block for %s.\n", - __FUNCTION__, hgStagingDir.c_str()); - } - - mHGStagingDir = hgStagingDir; - - /* Provide URIs for each path in the guest's file list. */ - if (FCP_TARGET_INFO_GNOME_COPIED_FILES == info) { - mHGCopiedUriList = "copy\n"; - pre = FCP_GNOME_LIST_PRE; - post = FCP_GNOME_LIST_POST; - } else if (FCP_TARGET_INFO_URI_LIST == info) { - pre = DND_URI_LIST_PRE_KDE; - post = DND_URI_LIST_POST; - } else { - Debug("%s: Unknown request target: %s\n", __FUNCTION__, - sd.get_target().c_str()); - return; - } - - /* Provide path within vmblock file system instead of actual path. */ - stagingDirName = GetLastDirName(hgStagingDir); - if (0 == stagingDirName.bytes()) { - Debug("%s: Can not get staging directory name\n", __FUNCTION__); - return; - } - - while ((str = GetNextPath(mHGFCPData, index).c_str()).bytes() != 0) { - Debug("%s: Path: %s", __FUNCTION__, str.c_str()); - mHGCopiedUriList += pre; - if (mBlockAdded) { - mHGCopiedUriList += mBlockCtrl->blockRoot; - mHGCopiedUriList += DIRSEPS + stagingDirName + DIRSEPS + str + post; - } else { - mHGCopiedUriList += DIRSEPS + hgStagingDir + DIRSEPS + str + post; - } - } - - /* Nautilus does not expect FCP_GNOME_LIST_POST after the last uri. See bug 143147. */ - if (FCP_TARGET_INFO_GNOME_COPIED_FILES == info) { - mHGCopiedUriList.erase(mHGCopiedUriList.size() - 1, 1); - } - } - - if (0 == mHGCopiedUriList.bytes()) { - Debug("%s: Can not get uri list\n", __FUNCTION__); - return; - } - - if (!mBlockAdded) { - /* - * If there is no blocking driver, wait here till file copy is done. - * 2 reasons to keep this: - * 1. If run vmware-user stand-alone as non-root, blocking driver can - * not be opened. Debug purpose only. - * 2. Other platforms (Solaris, etc) may also use this code, - * and there is no blocking driver yet. - * - * Polling here will not be sufficient for large files (experiments - * showed it was sufficient for a 256MB file, and failed for a 1GB - * file, but those numbers are of course context-sensitive and so YMMV). - * The reason is we are executing in the context of gtkmm callback, and - * apparently it only has so much patience regarding how quickly we - * return. - */ - Debug("%s no blocking driver, waiting for " - "HG file copy done ... mFileTransferDone is %d\n", __FUNCTION__, - (int) mFileTransferDone); - while (mFileTransferDone == false) { - struct timeval tv; - int nr; - - tv.tv_sec = 0; - nr = EventManager_ProcessNext(gEventQueue, (uint64 *)&tv.tv_usec); - if (nr != 1) { - Debug("%s: unexpected end of loop: returned " - "value is %d.\n", __FUNCTION__, nr); - return; - } - if (select(0, NULL, NULL, NULL, &tv) == -1) { - Debug("%s: error in select (%s).\n", __FUNCTION__, - strerror(errno)); - return; - } - } - Debug("%s: file transfer done!\n", __FUNCTION__); - } - - Debug("%s: providing file list [%s]\n", __FUNCTION__, - mHGCopiedUriList.c_str()); - - sd.set(sd.get_target().c_str(), mHGCopiedUriList.c_str()); -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteUI::LocalGetTextOrRTFRequestCB -- - * - * Callback from a text or RTF paste request from another guest application. - * H->G copy paste only. - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -void -CopyPasteUI::LocalGetTextOrRTFRequestCB(Gtk::SelectionData& sd, // IN/OUT - guint info) // Ignored -{ - sd.set(sd.get_target().c_str(), ""); - - if (!mCP.IsCopyPasteAllowed()) { - return; - } - - const utf::string target = sd.get_target().c_str(); - - Debug("%s: Got paste request, target is %s\n", - __FUNCTION__, target.c_str()); - - if (target == TARGET_NAME_APPLICATION_RTF || - target == TARGET_NAME_TEXT_RICHTEXT) { - if (0 == mHGRTFData.bytes()) { - Debug("%s: Can not get valid RTF data\n", __FUNCTION__); - return; - } - - Debug("%s: providing RTF data, size %"FMTSZ"u\n", - __FUNCTION__, mHGRTFData.bytes()); - - sd.set(target.c_str(), mHGRTFData.c_str()); - } - - if (target == TARGET_NAME_STRING || - target == TARGET_NAME_TEXT_PLAIN || - target == TARGET_NAME_UTF8_STRING || - target == TARGET_NAME_COMPOUND_TEXT) { - if (0 == mHGTextData.bytes()) { - Debug("%s: Can not get valid text data\n", __FUNCTION__); - return; - } - Debug("%s: providing plain text, size %"FMTSZ"u\n", - __FUNCTION__, mHGTextData.bytes()); - - sd.set(target.c_str(), mHGTextData.c_str()); - } -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPaste::LocalClearClipboardCB -- - * - * Clear clipboard request from another host application. - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -void -CopyPasteUI::LocalClearClipboardCB(void) -{ - Debug("%s: got clear callback\n", __FUNCTION__); - mIsClipboardOwner = FALSE; -} - - -/* - *---------------------------------------------------------------------- - * - * CopyPasteUI::LocalClipboardTimestampCB -- - * - * Got the local clipboard timestamp. Ask for the primary timestamp. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -void -CopyPasteUI::LocalClipboardTimestampCB(const Gtk::SelectionData& sd) // IN -{ - int length = sd.get_length(); - Debug("%s: enter sd.get_length() %d.\n", __FUNCTION__, - length); - if (length == 4) { - mClipTime = ((uint32*) sd.get_data())[0]; - Debug("%s: mClipTime: %"FMT64"u.", __FUNCTION__, mClipTime); - } else if (length == 8) { - mClipTime = ((uint64*) sd.get_data())[0]; - Debug("%s: mClipTime: %"FMT64"u.", __FUNCTION__, mClipTime); - } else { - Debug("%s: Unable to get mClipTime.", __FUNCTION__); - } - - Glib::RefPtr refClipboard - = Gtk::Clipboard::get(GDK_SELECTION_PRIMARY); - refClipboard->request_contents(TARGET_NAME_TIMESTAMP, - sigc::mem_fun(this, &CopyPasteUI::LocalPrimTimestampCB)); -} - - -/* - *---------------------------------------------------------------------- - * - * CopyPasteUI::LocalPrimTimestampCB -- - * - * Got the local primary timestamp. Choose the most recently changed - * clipboard and get the selection from it. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -void -CopyPasteUI::LocalPrimTimestampCB(const Gtk::SelectionData& sd) // IN -{ - int length = sd.get_length(); - Debug("%s: enter sd.get_length() is %d.\n", __FUNCTION__, length); - if (length == 4) { - mPrimTime = ((uint32*) sd.get_data())[0]; - Debug("%s: mPrimTime: %"FMT64"u.", __FUNCTION__, mPrimTime); - } else if (length == 8) { - mPrimTime = ((uint64*) sd.get_data())[0]; - Debug("%s: mPrimTime: %"FMT64"u.", __FUNCTION__, mPrimTime); - } else { - Debug("%s: Unable to get mPrimTime.", __FUNCTION__); - } - - /* After got both timestamp, choose latest one as active selection. */ - mGHSelection = GDK_SELECTION_PRIMARY; - if (mClipTime > mPrimTime) { - mGHSelection = GDK_SELECTION_CLIPBOARD; - } - - Glib::RefPtr refClipboard; - bool flipped = false; -again: - bool validDataInClip = false; - refClipboard = Gtk::Clipboard::get(mGHSelection); - - Debug("%s: trying %s selection.\n", __FUNCTION__, - mGHSelection == GDK_SELECTION_PRIMARY ? "Primary" : "Clip"); - - CPClipboard_Clear(&mClipboard); - - /* First check for URIs. This must always be done first */ - bool haveURIs = false; - std::string format; - if (refClipboard->wait_is_target_available(FCP_TARGET_NAME_GNOME_COPIED_FILES)) { - format = FCP_TARGET_NAME_GNOME_COPIED_FILES; - haveURIs = true; - } else if (refClipboard->wait_is_target_available(FCP_TARGET_NAME_URI_LIST)) { - format = FCP_TARGET_NAME_URI_LIST; - haveURIs = true; - } - - if (haveURIs) { - refClipboard->request_contents(format, - sigc::mem_fun(this, - &CopyPasteUI::LocalReceivedFileListCB)); - return; - } - - /* Try to get image data from clipboard. */ - Glib::RefPtr img = refClipboard->wait_for_image(); - gsize bufSize; - if (img) { - gchar *buf = NULL; - - img->save_to_buffer(buf, bufSize, Glib::ustring("png")); - if (bufSize > 0 && - bufSize <= (int)CPCLIPITEM_MAX_SIZE_V3 && - CPClipboard_SetItem(&mClipboard, CPFORMAT_IMG_PNG, - buf, bufSize)) { - mCP.SetRemoteClipboard(&mClipboard); - Debug("%s: Got PNG: %"FMTSZ"u\n", __FUNCTION__, bufSize); - } else { - Debug("%s: Failed to get PNG\n", __FUNCTION__); - } - g_free(buf); - return; - } - - /* Try to get RTF data from clipboard. */ - bool haveRTF = false; - if (refClipboard->wait_is_target_available(TARGET_NAME_APPLICATION_RTF)) { - Debug("%s: RTF is available\n", __FUNCTION__); - format = TARGET_NAME_APPLICATION_RTF; - haveRTF = true; - } - if (refClipboard->wait_is_target_available(TARGET_NAME_TEXT_RICHTEXT)) { - Debug("%s: RICHTEXT is available\n", __FUNCTION__); - format = TARGET_NAME_TEXT_RICHTEXT; - haveRTF = true; - } - - if (haveRTF) { - /* - * There is a function for waiting for rtf data, but that was leading - * to crashes. It's use required we instantiate a class that implements - * Gtk::TextBuffer and then query that class for a reference to it's - * TextBuffer instance. This all compiled fine but crashed in testing - * so we opt to use the more generic API here which seemed more stable. - */ - Gtk::SelectionData sdata = refClipboard->wait_for_contents(format); - bufSize = sdata.get_length(); - if (bufSize > 0 && - bufSize <= (int)CPCLIPITEM_MAX_SIZE_V3 && - CPClipboard_SetItem(&mClipboard, CPFORMAT_RTF, - (const void *)sdata.get_data(), bufSize + 1)) { - validDataInClip = true; - Debug("%s: Got RTF\n", __FUNCTION__); - } else { - Debug("%s: Failed to get RTF size %d max %d\n", - __FUNCTION__, (int) bufSize, (int)CPCLIPITEM_MAX_SIZE_V3); - } - } - - /* Try to get Text data from clipboard. */ - if (refClipboard->wait_is_text_available()) { - Debug("%s: ask for text\n", __FUNCTION__); - Glib::ustring str = refClipboard->wait_for_text(); - bufSize = str.bytes(); - if (bufSize > 0 && - bufSize <= (int)CPCLIPITEM_MAX_SIZE_V3 && - CPClipboard_SetItem(&mClipboard, CPFORMAT_TEXT, - (const void *)str.data(), bufSize + 1)) { - validDataInClip = true; - Debug("%s: Got TEXT: %"FMTSZ"u\n", __FUNCTION__, bufSize); - } else { - Debug("%s: Failed to get TEXT\n", __FUNCTION__); - } - } - - if (validDataInClip) { - /* - * RTF or text data (or both) in the clipboard. - */ - mCP.SetRemoteClipboard(&mClipboard); - } else if (!flipped) { - /* - * If we get here, we got nothing (no image, URI, text) so - * try the other selection. - */ - Debug("%s: got nothing for this selection, try the other.\n", - __FUNCTION__); - mGHSelection = mGHSelection == GDK_SELECTION_PRIMARY ? - GDK_SELECTION_CLIPBOARD : GDK_SELECTION_PRIMARY; - flipped = true; - goto again; - } -} - - -/* - *---------------------------------------------------------------------- - * - * CopyPasteUI::LocalReceivedFileListCB -- - * - * Got clipboard or primary selection file list. Parse it and add - * it to the crossplaform clipboard. Send clipboard to the host. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -void -CopyPasteUI::LocalReceivedFileListCB(const Gtk::SelectionData& sd) // IN -{ - Debug("%s: enter", __FUNCTION__); - const utf::string target = sd.get_target().c_str(); - - if (target == FCP_TARGET_NAME_GNOME_COPIED_FILES || - target == FCP_TARGET_NAME_URI_LIST) { - LocalGetSelectionFileList(sd); - mCP.SetRemoteClipboard(&mClipboard); - } -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteUI::LocalGetFileContentsRequestCB -- - * - * Callback from a file paste request from another guest application. - * Return the file list. - * - * H->G only. - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -void -CopyPasteUI::LocalGetFileContentsRequestCB(Gtk::SelectionData& sd, // IN - guint info) // IN -{ - std::vector::const_iterator iter; - utf::string uriList = ""; - utf::string pre; - utf::string post; - - sd.set(sd.get_target().c_str(), ""); - - /* Provide URIs for each path in the guest's file list. */ - if (FCP_TARGET_INFO_GNOME_COPIED_FILES == info) { - uriList = "copy\n"; - pre = FCP_GNOME_LIST_PRE; - post = FCP_GNOME_LIST_POST; - } else if (FCP_TARGET_INFO_URI_LIST == info) { - pre = DND_URI_LIST_PRE_KDE; - post = DND_URI_LIST_POST; - } else { - Debug("%s: Unknown request target: %s\n", - __FUNCTION__, sd.get_target().c_str()); - return; - } - - for (iter = mHGFileContentsList.begin(); - iter != mHGFileContentsList.end(); - iter++) { - uriList += pre + *iter + post; - } - - /* Nautilus does not expect FCP_GNOME_LIST_POST after the last uri. See bug 143147. */ - if (FCP_TARGET_INFO_GNOME_COPIED_FILES == info) { - uriList.erase(uriList.size() - 1, 1); - } - - if (0 == uriList.bytes()) { - Debug("%s: Can not get uri list\n", __FUNCTION__); - return; - } - - Debug("%s: providing file list [%s]\n", __FUNCTION__, uriList.c_str()); - - sd.set(sd.get_target().c_str(), uriList.c_str()); -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteUI::LocalGetSelectionFileList -- - * - * Construct local file list and remote file list from selection data. - * Called by both DnD and FCP. - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -void -CopyPasteUI::LocalGetSelectionFileList(const Gtk::SelectionData& sd) // IN -{ - utf::string source; - char *newPath; - char *newRelPath; - size_t newPathLen; - size_t index = 0; - DnDFileList fileList; - DynBuf buf; - uint64 totalSize = 0; - int64 size; - - /* - * Turn the uri list into two \0 delimited lists. One for full paths and - * one for just the last path component. - */ - source = sd.get_data_as_string().c_str(); - Debug("%s: Got file list: [%s]\n", __FUNCTION__, source.c_str()); - - /* - * In gnome, before file list there may be a extra line indicating it - * is a copy or cut. - */ - if (source.startsWith("copy\n")) { - source = source.erase(0, 5); - } - - if (source.startsWith("cut\n")) { - source = source.erase(0, 4); - } - - while (source.bytes() > 0 && - (source[0] == '\n' || source[0] == '\r' || source[0] == ' ')) { - source = source.erase(0, 1); - } - - while ((newPath = DnD_UriListGetNextFile(source.c_str(), - &index, - &newPathLen)) != NULL) { - - /* - * Parse relative path. - */ - newRelPath = Str_Strrchr(newPath, DIRSEPC) + 1; // Point to char after '/' - - /* - * XXX For directory, value is -1, so if there is any directory, - * total size is not accurate. - */ - if ((size = File_GetSize(newPath)) >= 0) { - totalSize += size; - } else { - Debug("%s: Unable to get file size for %s\n", __FUNCTION__, newPath); - } - - Debug("%s: Adding newPath '%s' newRelPath '%s'\n", __FUNCTION__, - newPath, newRelPath); - fileList.AddFile(newPath, newRelPath); - free(newPath); - } - - DynBuf_Init(&buf); - fileList.SetFileSize(totalSize); - Debug("%s: totalSize is %"FMT64"u\n", __FUNCTION__, totalSize); - fileList.ToCPClipboard(&buf, false); - CPClipboard_SetItem(&mClipboard, CPFORMAT_FILELIST, DynBuf_Get(&buf), - DynBuf_GetSize(&buf)); - DynBuf_Destroy(&buf); -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteUI::GetLastDirName -- - * - * Try to get last directory name from a full path name. - * - * Results: - * Last dir name in the full path name if sucess, empty str otherwise - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -utf::string -CopyPasteUI::GetLastDirName(const utf::string &str) // IN -{ - utf::string ret; - size_t start; - size_t end; - - end = str.bytes() - 1; - if (end >= 0 && DIRSEPC == str[end]) { - end--; - } - - if (end <= 0 || str[0] != DIRSEPC) { - return ""; - } - - start = end; - - while (str[start] != DIRSEPC) { - start--; - } - - return str.substr(start + 1, end - start); -} - - -/* - *---------------------------------------------------------------------------- - * - * CopyPasteUI::GetNextPath -- - * - * Provides a substring containing the next path from the provided - * NUL-delimited string starting at the provided index. - * - * Results: - * A string with the next path or "" if there are no more paths. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -utf::utf8string -CopyPasteUI::GetNextPath(utf::utf8string& str, // IN: NUL-delimited path list - size_t& index) // IN/OUT: current index into string -{ - utf::utf8string ret; - size_t start; - - if (index >= str.length()) { - return ""; - } - - for (start = index; str[index] != '\0' && index < str.length(); index++) { - /* - * Escape reserved characters according to RFC 1630. We'd use - * Escape_Do() if this wasn't a utf::string, but let's use the same table - * replacement approach. - */ - static char const Dec2Hex[] = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', - }; - - unsigned char ubyte = str[index]; - - if (ubyte == '#' || /* Fragment identifier delimiter */ - ubyte == '?' || /* Query string delimiter */ - ubyte == '*' || /* "Special significance within specific schemes" */ - ubyte == '!' || /* "Special significance within specific schemes" */ - ubyte == '%' || /* Escape character */ - ubyte >= 0x80) { /* UTF-8 encoding bytes */ - str.replace(index, 1, "%"); - str.insert(index + 1, 1, Dec2Hex[ubyte >> 4]); - str.insert(index + 2, 1, Dec2Hex[ubyte & 0xF]); - index += 2; - } - } - - ret = str.substr(start, index - start); - Debug("%s: nextpath: %s", __FUNCTION__, ret.c_str()); - index++; - return ret; -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteUI::GetRemoteClipboardCB -- - * - * Invoked when got data from host. Update the internal data to get the file - * names or the text that needs to be transferred. - * - * Method for copy and paste from host to guest. - * - * Results: - * None - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -void -CopyPasteUI::GetRemoteClipboardCB(const CPClipboard *clip) // IN -{ - Glib::RefPtr refClipboard = - Gtk::Clipboard::get(GDK_SELECTION_CLIPBOARD); - Glib::RefPtr refPrimary = - Gtk::Clipboard::get(GDK_SELECTION_PRIMARY); - void *buf; - size_t sz; - - Debug("%s: enter\n", __FUNCTION__); - if (!clip) { - Debug("%s: No clipboard contents.", __FUNCTION__); - return; - } - - /* Clear the clipboard contents if we are the owner. */ - if (mIsClipboardOwner) { - refClipboard->clear(); - refPrimary->clear(); - mIsClipboardOwner = FALSE; - Debug("%s: Cleared local clipboard", __FUNCTION__); - } - - mHGTextData.clear(); - mHGRTFData.clear(); - mHGFCPData.clear(); - - if (CPClipboard_ItemExists(clip, CPFORMAT_TEXT) || - CPClipboard_ItemExists(clip, CPFORMAT_RTF)) { - std::list targets; - - if (CPClipboard_GetItem(clip, CPFORMAT_TEXT, &buf, &sz)) { - Gtk::TargetEntry stringText(TARGET_NAME_STRING); - Gtk::TargetEntry plainText(TARGET_NAME_TEXT_PLAIN); - Gtk::TargetEntry utf8Text(TARGET_NAME_UTF8_STRING); - Gtk::TargetEntry compountText(TARGET_NAME_COMPOUND_TEXT); - - Debug("%s: Text data, size %"FMTSZ"u.\n", __FUNCTION__, sz); - targets.push_back(stringText); - targets.push_back(plainText); - targets.push_back(utf8Text); - targets.push_back(compountText); - mHGTextData = utf::string((const char *)buf, STRING_ENCODING_UTF8); - - mIsClipboardOwner = TRUE; - } - - if (CPClipboard_GetItem(clip, CPFORMAT_RTF, &buf, &sz)) { - Debug("%s: RTF data, size %"FMTSZ"u.\n", __FUNCTION__, sz); - Gtk::TargetEntry appRtf(TARGET_NAME_APPLICATION_RTF); - Gtk::TargetEntry textRtf(TARGET_NAME_TEXT_RICHTEXT); - - targets.push_back(appRtf); - targets.push_back(textRtf); - mHGRTFData = utf::string((const char *)buf, STRING_ENCODING_UTF8); - - mIsClipboardOwner = TRUE; - } - - refClipboard->set(targets, - sigc::mem_fun(this, &CopyPasteUI::LocalGetTextOrRTFRequestCB), - sigc::mem_fun(this, &CopyPasteUI::LocalClearClipboardCB)); - refPrimary->set(targets, - sigc::mem_fun(this, &CopyPasteUI::LocalGetTextOrRTFRequestCB), - sigc::mem_fun(this, &CopyPasteUI::LocalClearClipboardCB)); - return; - } - - if (CPClipboard_GetItem(clip, CPFORMAT_IMG_PNG, &buf, &sz)) { - Debug("%s: PNG data, size %"FMTSZ"u.\n", __FUNCTION__, sz); - /* Try to load buf into pixbuf, and write to local clipboard. */ - Glib::RefPtr loader = Gdk::PixbufLoader::create(); - - if (loader) { - loader->write((const guint8 *)buf, sz); - loader->close(); - - refClipboard->set_image(loader->get_pixbuf()); - refPrimary->set_image(loader->get_pixbuf()); - } - return; - } - if (CPClipboard_GetItem(clip, CPFORMAT_FILELIST, &buf, &sz)) { - Debug("%s: File data.\n", __FUNCTION__); - DnDFileList flist; - flist.FromCPClipboard(buf, sz); - mHGFCPData = flist.GetRelPathsStr(); - - refClipboard->set(mListTargets, - sigc::mem_fun(this, &CopyPasteUI::LocalGetFileRequestCB), - sigc::mem_fun(this, &CopyPasteUI::LocalClearClipboardCB)); - refPrimary->set(mListTargets, - sigc::mem_fun(this, &CopyPasteUI::LocalGetFileRequestCB), - sigc::mem_fun(this, &CopyPasteUI::LocalClearClipboardCB)); - - mIsClipboardOwner = TRUE; - mHGGetListTime = GetCurrentTime(); - mHGGetFilesInitiated = false; - mHGCopiedUriList = ""; - } - - if (CPClipboard_ItemExists(clip, CPFORMAT_FILECONTENTS)) { - Debug("%s: File contents data\n", __FUNCTION__); - if (LocalPrepareFileContents(clip)) { - refClipboard->set(mListTargets, - sigc::mem_fun(this, &CopyPasteUI::LocalGetFileContentsRequestCB), - sigc::mem_fun(this, &CopyPasteUI::LocalClearClipboardCB)); - refPrimary->set(mListTargets, - sigc::mem_fun(this, &CopyPasteUI::LocalGetFileContentsRequestCB), - sigc::mem_fun(this, &CopyPasteUI::LocalClearClipboardCB)); - mIsClipboardOwner = TRUE; - } - } -} - - -/* - *---------------------------------------------------------------------------- - * - * CopyPasteUI::LocalPrepareFileContents -- - * - * Try to extract file contents from mClipboard. Write all files to a - * temporary staging directory. Construct uri list. - * - * Results: - * true if success, false otherwise. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -bool -CopyPasteUI::LocalPrepareFileContents(const CPClipboard *clip) // IN -{ - void *buf = NULL; - size_t sz = 0; - XDR xdrs; - CPFileContents fileContents; - CPFileContentsList *contentsList = NULL; - size_t nFiles = 0; - CPFileItem *fileItem = NULL; - Unicode tempDir = NULL; - size_t i = 0; - bool ret = false; - - if (!CPClipboard_GetItem(clip, CPFORMAT_FILECONTENTS, &buf, &sz)) { - Debug("%s: CPClipboard_GetItem failed\n", __FUNCTION__); - return false; - } - - /* Extract file contents from buf. */ - xdrmem_create(&xdrs, (char *)buf, sz, XDR_DECODE); - memset(&fileContents, 0, sizeof fileContents); - - if (!xdr_CPFileContents(&xdrs, &fileContents)) { - Debug("%s: xdr_CPFileContents failed.\n", __FUNCTION__); - xdr_destroy(&xdrs); - return false; - } - xdr_destroy(&xdrs); - - contentsList = fileContents.CPFileContents_u.fileContentsV1; - if (!contentsList) { - Debug("%s: invalid contentsList.\n", __FUNCTION__); - goto exit; - } - - nFiles = contentsList->fileItem.fileItem_len; - if (0 == nFiles) { - Debug("%s: invalid nFiles.\n", __FUNCTION__); - goto exit; - } - - fileItem = contentsList->fileItem.fileItem_val; - if (!fileItem) { - Debug("%s: invalid fileItem.\n", __FUNCTION__); - goto exit; - } - - /* - * Write files into a temporary staging directory. These files will be moved - * to final destination, or deleted on next reboot. - */ - tempDir = DnD_CreateStagingDirectory(); - if (!tempDir) { - Debug("%s: DnD_CreateStagingDirectory failed.\n", __FUNCTION__); - goto exit; - } - - mHGFileContentsList.clear(); - - for (i = 0; i < nFiles; i++) { - utf::string fileName; - utf::string filePathName; - VmTimeType createTime = -1; - VmTimeType accessTime = -1; - VmTimeType writeTime = -1; - VmTimeType attrChangeTime = -1; - - if (!fileItem[i].cpName.cpName_val || - 0 == fileItem[i].cpName.cpName_len) { - Debug("%s: invalid fileItem[%"FMTSZ"u].cpName.\n", __FUNCTION__, i); - goto exit; - } - - /* - * '\0' is used as directory separator in cross-platform name. Now turn - * '\0' in data into DIRSEPC. - * - * Note that we don't convert the final '\0' into DIRSEPC so the string - * is NUL terminated. - */ - CPNameUtil_CharReplace(fileItem[i].cpName.cpName_val, - fileItem[i].cpName.cpName_len - 1, - '\0', - DIRSEPC); - fileName = fileItem[i].cpName.cpName_val; - filePathName = tempDir; - filePathName += DIRSEPS + fileName; - - if (fileItem[i].validFlags & CP_FILE_VALID_TYPE && - CP_FILE_TYPE_DIRECTORY == fileItem[i].type) { - if (!File_CreateDirectory(filePathName.c_str())) { - goto exit; - } - Debug("%s: created directory [%s].\n", - __FUNCTION__, filePathName.c_str()); - } else if (fileItem[i].validFlags & CP_FILE_VALID_TYPE && - CP_FILE_TYPE_REGULAR == fileItem[i].type) { - FileIODescriptor file; - FileIOResult fileErr; - - FileIO_Invalidate(&file); - - fileErr = FileIO_Open(&file, - filePathName.c_str(), - FILEIO_ACCESS_WRITE, - FILEIO_OPEN_CREATE_EMPTY); - if (!FileIO_IsSuccess(fileErr)) { - goto exit; - } - - fileErr = FileIO_Write(&file, - fileItem[i].content.content_val, - fileItem[i].content.content_len, - NULL); - - FileIO_Close(&file); - Debug("%s: created file [%s].\n", __FUNCTION__, filePathName.c_str()); - } else { - /* - * Right now only Windows can provide CPFORMAT_FILECONTENTS data. - * Symlink file is not expected. Continue with next file if the - * type is not valid. - */ - continue; - } - - /* Update file time attributes. */ - createTime = fileItem->validFlags & CP_FILE_VALID_CREATE_TIME ? - fileItem->createTime: -1; - accessTime = fileItem->validFlags & CP_FILE_VALID_ACCESS_TIME ? - fileItem->accessTime: -1; - writeTime = fileItem->validFlags & CP_FILE_VALID_WRITE_TIME ? - fileItem->writeTime: -1; - attrChangeTime = fileItem->validFlags & CP_FILE_VALID_CHANGE_TIME ? - fileItem->attrChangeTime: -1; - - if (!File_SetTimes(filePathName.c_str(), - createTime, - accessTime, - writeTime, - attrChangeTime)) { - /* Not a critical error, only log it. */ - Debug("%s: File_SetTimes failed with file [%s].\n", - __FUNCTION__, filePathName.c_str()); - } - - /* Update file permission attributes. */ - if (fileItem->validFlags & CP_FILE_VALID_PERMS) { - if (Posix_Chmod(filePathName.c_str(), - fileItem->permissions) < 0) { - /* Not a critical error, only log it. */ - Debug("%s: Posix_Chmod failed with file [%s].\n", - __FUNCTION__, filePathName.c_str()); - } - } - - /* - * If there is no DIRSEPC inside the fileName, this file/directory is a - * top level one. We only put top level name into uri list. - */ - if (fileName.find(DIRSEPS, 0) == utf::string::npos) { - mHGFileContentsList.push_back(filePathName); - } - } - Debug("%s: created uri list\n", __FUNCTION__); - ret = true; - -exit: - xdr_free((xdrproc_t)xdr_CPFileContents, (char *)&fileContents); - if (tempDir && !ret) { - DnD_DeleteStagingFiles(tempDir, FALSE); - } - free(tempDir); - return ret; -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteUI::GetLocalFilesDone -- - * - * Callback when CopyPasteUI::GetLocalFiles is done, which finishes the file - * copying from host to guest staging directory. This function notifies - * the Copy/Paste data object and end its waiting state in order to continue - * the file copying from local staging directory to local target directory. - * - * Results: - * None - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -void -CopyPasteUI::GetLocalFilesDone(bool success) -{ - Debug("%s: enter success %d\n", __FUNCTION__, success); - - if (mBlockAdded) { - Debug("%s: removing block for %s\n", __FUNCTION__, mHGStagingDir.c_str()); - mBlockCtrl->RemoveBlock(mBlockCtrl->fd, mHGStagingDir.c_str()); - mBlockAdded = false; - } - - mFileTransferDone = true; - if (success) { - /* - * Mark current staging dir to be deleted on next reboot for FCP. The - * file will not be deleted after reboot if it is moved to another - * location by target application. - */ - DnD_DeleteStagingFiles(mHGStagingDir.c_str(), TRUE); - } else { - /* Copied files are already removed in common layer. */ - mHGStagingDir.clear(); - } - mHGGetFilesInitiated = false; -} - - -/* - *----------------------------------------------------------------------------- - * - * CopyPasteUI::Reset -- - * - * - * Results: - * None - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -void -CopyPasteUI::Reset(void) -{ - Debug("%s: enter\n", __FUNCTION__); - /* Cancel any pending file transfer. */ -} - diff --git a/open-vm-tools/vmware-user/copyPasteUI.h b/open-vm-tools/vmware-user/copyPasteUI.h deleted file mode 100644 index 5bb4a7907..000000000 --- a/open-vm-tools/vmware-user/copyPasteUI.h +++ /dev/null @@ -1,118 +0,0 @@ -/********************************************************* - * Copyright (C) 2009 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - -/** - * @file copyPasteUI.h - * - * This class implements the methods that allows Copy/Paste - * between host and guest using version 3+ of the protocol. - * - */ - -#ifndef COPYPASTE_UI_H -#define COPYPASTE_UI_H - -#include "stringxx/string.hh" - -extern "C" { -#include "dnd.h" -#include "debug.h" -#include "str.h" -#include "dndClipboard.h" -#include "dynbuf.h" -#include "../dnd/dndFileContentsUtil.h" -#include "dynxdr.h" -#include "cpNameUtil.h" -#include "posix.h" -} - -#include "unicodeOperations.h" - -#include "copyPaste.hh" - -#include -#include -#include - -class CopyPasteUI : public sigc::trackable -{ -public: - CopyPasteUI(); - virtual ~CopyPasteUI(); - bool Init(); - void VmxCopyPasteVersionChanged(struct RpcIn *rpcIn, - uint32 version); - void SetCopyPasteAllowed(bool isCopyPasteAllowed) - { mCP.SetCopyPasteAllowed(isCopyPasteAllowed); } - void Reset(void); - void Cancel(void); - void SetBlockControl(DnDBlockControl *blockCtrl) - { Debug("Setting mBlockCtrl to %p\n", blockCtrl); - mBlockCtrl = blockCtrl; } - -private: - - /* hg */ - void GetRemoteClipboardCB(const CPClipboard *clip); - void RemoteGetFilesDone(void); - void LocalGetFileRequestCB(Gtk::SelectionData& selection_data, guint info); - void LocalGetTextOrRTFRequestCB(Gtk::SelectionData& sd, guint info); - void LocalGetSelectionFileList(const Gtk::SelectionData& sd); - void LocalGetFileContentsRequestCB(Gtk::SelectionData& sd, guint info); - void LocalClearClipboardCB(void); - - /* gh */ - bool GetLocalClipboard(CPClipboard *clip); - void LocalClipboardTimestampCB(const Gtk::SelectionData& sd); - void LocalPrimTimestampCB(const Gtk::SelectionData& sd); - void LocalReceivedFileListCB(const Gtk::SelectionData& selection_data); - void GetLocalFilesDone(bool success); - - /* Conversion methods. */ - utf::utf8string GetNextPath(utf::utf8string &str, size_t& index); - utf::string GetLastDirName(const utf::string &str); - bool LocalPrepareFileContents(const CPClipboard *clip); - - VmTimeType GetCurrentTime(void); - - // Member variables - CopyPaste mCP; - bool mClipboardEmpty; - utf::string mHGStagingDir; - std::list mListTargets; - bool mIsClipboardOwner; - uint64 mClipTime; - uint64 mPrimTime; - GdkAtom mGHSelection; - CPClipboard mClipboard; - - /* File vars. */ - bool mHGGetFilesInitiated; - VmTimeType mHGGetListTime; - utf::string mHGCopiedUriList; - utf::utf8string mHGFCPData; - utf::string mHGTextData; - utf::string mHGRTFData; - std::vector mHGFileContentsList; - bool mFileTransferDone; - bool mBlockAdded; - DnDBlockControl *mBlockCtrl; - bool mInited; -}; - -#endif // COPYPASTE_UI_H diff --git a/open-vm-tools/vmware-user/debugStdio.c b/open-vm-tools/vmware-user/debugStdio.c deleted file mode 100644 index c8ead38bf..000000000 --- a/open-vm-tools/vmware-user/debugStdio.c +++ /dev/null @@ -1,244 +0,0 @@ -/********************************************************* - * Copyright (C) 1998 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - - -/* - * debug.c -- - * - * Platform specific debug routines - * - */ - -#include -#include -#include -#include -#if defined(_WIN32) && defined(_MSC_VER) -# include -#endif -#if defined(N_PLAT_NLM) -# include "vmwtool.h" -#endif - - -#include "vmware.h" -#include "debug.h" -#include "util.h" -#include "str.h" -#include "fileIO.h" -#include "file.h" -#include "system.h" -#include "unicode.h" - -static char debugFile[FILE_MAXPATH] = {0}; -static Bool debugEnabled = FALSE; -static const char *debugPrefix = NULL; - - -/* - *----------------------------------------------------------------------------- - * - * Debug_Set -- - * - * Enable/Disable debugging output - * - * Result - * None. - * - * Side-effects - * None. - * - *----------------------------------------------------------------------------- - */ - -void -Debug_Set(Bool enable, // IN - const char *prefix) // IN -{ - debugEnabled = enable; - debugPrefix = prefix; -} - - -/* - *----------------------------------------------------------------------------- - * - * Debug_EnableToFile -- - * - * Enable debugging output to the given file. If backup is TRUE, will rename - * existing file to file.old and start logging to a new file. Only daemon - * should set backup flag then will do backup for each reboot. - * - * Result - * None. - * - * Side-effects - * None. - * - *----------------------------------------------------------------------------- - */ - -void -Debug_EnableToFile(const char *file, // IN - Bool backup) // IN -{ - if (backup && file && File_Exists(file)) { - /* Back up existing log file. */ - char *bakFile = Str_Asprintf(NULL, "%s.old", file); - if (bakFile && - !File_IsDirectory(bakFile) && - 0 == File_UnlinkIfExists(bakFile)) { // remove old back up file. - File_Rename(file, bakFile); - } - free(bakFile); - } - if (file) { - Str_Sprintf(debugFile, sizeof debugFile, "%s", file); - debugEnabled = TRUE; - } else { - debugFile[0] = '\0'; - } -} - - -/* - *----------------------------------------------------------------------------- - * - * DebugToFile -- - * - * Print a string to the given file. This opens & closes the file - * handle each time it is called so it will significantly - * slow down the calling program. This is done so that the file - * can be opened & read while the program is running. - * - * Results: - * None. - * - * Side effects: - * DebugToFile is turned off if there was an error opening the file. - * - *----------------------------------------------------------------------------- - */ - -static -void DebugToFile(const char *str) // IN -{ -#ifndef _CONSOLE - FileIOResult fr; - FileIODescriptor fd; - size_t bytesWritten; - Unicode timePrefix; - const char *timePrefixUtf8; - char debugFileShadow; - - ASSERT(debugFile[0] != 0); - - FileIO_Invalidate(&fd); - - fr = FileIO_Open(&fd, debugFile, FILEIO_OPEN_ACCESS_WRITE, - FILEIO_OPEN_CREATE); - debugFileShadow = debugFile[0]; - debugFile[0] = '\0'; - if (fr != FILEIO_SUCCESS) { - Warning("---Error opening file '%s'.\n", debugFile); - goto done; - } - - /* - * XXX: Writing the date/time prefix in UTF-8 and the rest of the string in - * an unspecified encoding is rather broken, but it'll have to do until the - * rest of the Tools are made internationalization-safe. - */ - timePrefix = System_GetTimeAsString(); - if (timePrefix == NULL) { - Warning("---Error getting formatted time string.\n"); - goto close; - } - timePrefixUtf8 = UTF8(timePrefix); - ASSERT(timePrefixUtf8); - - FileIO_Seek(&fd, 0, FILEIO_SEEK_END); - fr = FileIO_Write(&fd, timePrefixUtf8, strlen(timePrefixUtf8), &bytesWritten); - fr = FileIO_Write(&fd, ": ", 2, &bytesWritten); - fr = FileIO_Write(&fd, str, strlen(str), &bytesWritten); - Unicode_Free(timePrefix); - if (fr != FILEIO_SUCCESS) { - Warning("---Error writing to file '%s'.\n", debugFile); - } - - close: - FileIO_Close(&fd); - - done: - debugFile[0] = debugFileShadow; - return; -#endif // _CONSOLE -} - - -/* - *----------------------------------------------------------------------------- - * - * Debug -- - * - * If debugging is enabled, output debug information - * - * Result - * None. - * - * Side-effects - * None. - * - *----------------------------------------------------------------------------- - */ - -void -Debug(char const *fmt, // IN: Format string - ...) // IN: Arguments -{ - va_list args; - char *str; - char *msg; - - if (debugEnabled == FALSE) { - return; - } - - va_start(args, fmt); - msg = Str_Vasprintf(NULL, fmt, args); - va_end(args); - - str = Str_Asprintf(NULL, "[%s]: %s", debugPrefix ? debugPrefix : "NULL", msg); - free(msg); - -#ifdef N_PLAT_NLM - OutputToScreenWithAttribute(VMwareScreen, BOLD_RED, "%s", str); -#else -#ifdef _WIN32 - OutputDebugString(str); -#endif -#if !defined(_WIN32) || defined(_CONSOLE) - fputs(str, stderr); -#endif -#endif - if (debugFile[0] != '\0') { - DebugToFile(str); - } - - free(str); -} diff --git a/open-vm-tools/vmware-user/dnd.c b/open-vm-tools/vmware-user/dnd.c deleted file mode 100644 index 5a4fa0565..000000000 --- a/open-vm-tools/vmware-user/dnd.c +++ /dev/null @@ -1,2710 +0,0 @@ -/********************************************************* - * Copyright (C) 2005 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - -/* - * dnd.c -- - * - * Handles the guest side of host<->guest DnD operations. - * - * Guest->Host DnD - * --------------- - * - * The DnD process within the guest starts when we receive a "dnd.ungrab" RPC - * message from the host, which invokes DnDRpcInMouseUngrabCB(). The MKS - * sends this RPC when it sees the mouse stray outside of the clip (guest's - * viewable area). RpcInMouseUngrabCB() will determine whether a DnD is - * pending by calling DnDDragPending(): - * o if a DnD is not pending, it replies with a "dnd.notpending" RPC and we - * are done, - * o if a DnD is pending, we send fake X events to the X server that place - * our invisible window at the location of the mouse pointer and generate - * mouse movements over the window. - * - * Faking mouse movement over our window causes Gtk to send us a "drag_motion" - * signal, which invokes DnDGtkDragMotionCB(). Here we find a common target - * (drop type) and request the data from the drag source via - * gtk_drag_get_data(). - * - * When the data is ready, Gtk signals us with a "data_received" signal. We - * parse the provided data and send the file names to the host with - * a "dnd.data.set" RPC. Then we start the DnD operation with a "dnd.enter" - * RPC. Upon receiving the "dnd.enter", the MKS will allow the ungrab of the - * mouse from the guest window and the user will be able to select a location - * to drop the files. - * - * (Note that it is important that the guest reply to the "dnd.ungrab" with - * either a "dnd.notpending" or a "dnd.enter" in a timely manner, since the - * MKS will delay mouse packets until it has received a reply from the - * guest.) - * - * When the user drops the files, the host will send us a "dnd.data.get.file" - * for each file, which invokes DnDRpcInGetNextFileCB(). On each invocation, - * we reply with the next file from the Guest->Host file list (obtained from - * DnDGHFileListGetNext()), and "|end|" when there are no more files. With - * this information, the host copies the files from the guest using HGFS. - * - * When the host has finished copying the files, it sends us a "dnd.finish" - * RPC, which invokes DnDRpcInFinishCB(). At this point, we fake X events - * that cause a mouse button release over our window. - * - * This button release causes Gtk to send us a "drag_drop" signal, which - * invokes DnDGtkDragDropCB(). Here we simply clean up our state and - * indicate that the drag finished successfully by calling gtk_drag_finish(). - * - * If an error occurs at any point, the host sends us a "dnd.finish cancel" - * RPC. We will fake an ESC key press and release to cancel the pending DnD - * in the guest. - * - * - * Host->Guest DnD - * --------------- - * - * A host->guest DnD begins with a "dnd.data.set" from the vmx to provide the - * list of files being dragged into the guest, then a "dnd.enter" to begin the - * DnD operation. When the "dnd.enter" is received, this process will send - * a fake mouse button press and mouse movement on its window, starting the - * DnD operation within the guest. At this point the mouse still has not been - * grabbed by the guest and all mouse movements go only to the host. - * - * As part of the normal DnD protocol on the host, the UI in the host will - * receive updates on the location of the mouse within its target window. - * This location is translated to guest coordinates and sent to us via the - * "dnd.move" RPC, at which point we fake additional mouse movements to that - * location. When the user releases the mouse, the host UI is again notified - * and sends us a "dnd.drop" RPC. - * - * When the drop occurs, we add a block (via vmblock) on the directory - * containing the files to be given to the target application, then fake - * a mouse release at the location of the drop. This will cause the target - * application to request the data, which we provide through our - * "drag_data_get" handler (DnDGtkDataRequestCB()). When the application - * attempts to access these files it will be blocked by vmblock. - * - * After the drop is sent, the host will send the files to the hgfs server - * running inside this process, and will notify us when that transfer is - * complete via the "dnd.data.finish" RPC. If the transfer is successful, we - * remove the block to allow the target application to access the files. If - * the transfer is unsuccessful, we remove any partially copied files then - * remove the block; this has the effect of failing the DnD operation since - * the target cannot access the necessary files. Once this is done, we - * generate a new file root within the staging directory and send that to the - * host for the next DnD operation. - * - * Note that we used to fake the mouse release only after the data transfer - * completed (and Windows guests still behave that way), but this was changed - * since the Linux UI was modified to allow guest interaction while the - * progress dialog (for the file transfer) was displayed and updating. This - * caused a lot of instability since the mouse was no longer in a predictable - * state when the fake release was sent. vmblock let us work around this by - * changing where the block occurred. - */ - - -#include -#include -#include /* for XTest*() */ -#include /* for XK_Escape */ -#include /* for XA_WINDOW */ - -#include "vmwareuserInt.h" -#include "vm_assert.h" -#include "vm_basic_defs.h" -#include "eventManager.h" -#include "debug.h" -#include "strutil.h" -#include "str.h" -#include "file.h" -#include "guestApp.h" -#include "cpName.h" -#include "cpNameUtil.h" -#include "dnd.h" -#include "util.h" -#include "hgfsVirtualDir.h" -#include "hgfsServerPolicy.h" -#include "vmblock.h" -#include "escape.h" -#include "vmware/guestrpc/tclodefs.h" - -#define DND_MAX_PATH 6144 -#define DRAG_TARGET_NAME_URI_LIST "text/uri-list" -#define DRAG_TARGET_INFO_URI_LIST 0 -#define DRAG_TARGET_NAME_TEXT_PLAIN "text/plain" -#define DRAG_TARGET_INFO_TEXT_PLAIN 1 -#define DRAG_TARGET_NAME_STRING "STRING" -#define DRAG_TARGET_INFO_STRING 2 -/* - * We support all three drag targets from Host->Guest since we can present - * filenames in any of these forms if an application requests. However, we - * only support file drag targets (text/uri-list) from Guest->Host since we - * can only DnD files across the backdoor. - */ -#define NR_DRAG_TARGETS 3 -#define NR_GH_DRAG_TARGETS 1 - -#define DROPEFFECT_NONE 0 -#define DROPEFFECT_COPY 1 -#define DROPEFFECT_MOVE 2 -#define DROPEFFECT_LINK 4 - -/* - * More friendly names for calling DnDFakeXEvents(). This is really ugly but - * it allows us to keep all of the X fake event code in one place. - * - * Operation | showWidget | buttonEvent | buttonPress | moveWindow | coordsProvided - * ----------+------------+-------------+-------------+------------+--------------- - * G->H Drag | Yes | No | n/a | Yes | No - * G->H Drop | No | Yes | Release | Yes | No - * H->G Drag | Yes | Yes | Press | Yes | No - * H->G Move | No | No | n/a | No | Yes - * H->G Drop | No | Yes | Release | No | Yes - * ----------+------------+-------------+-------------+------------+--------------- - */ -#define DnDGHFakeDrag(widget) \ - DnDFakeXEvents(widget, TRUE, FALSE, FALSE, TRUE, FALSE, 0, 0) -#define DnDGHFakeDrop(widget) \ - DnDFakeXEvents(widget, FALSE, TRUE, FALSE, TRUE, FALSE, 0, 0) -#define DnDHGFakeDrag(widget) \ - DnDFakeXEvents(widget, TRUE, TRUE, TRUE, TRUE, FALSE, 0, 0) -#define DnDHGFakeMove(widget, x, y) \ - DnDFakeXEvents(widget, FALSE, FALSE, FALSE, FALSE, TRUE, x, y) -#define DnDHGFakeDrop(widget, x, y) \ - DnDFakeXEvents(widget, FALSE, TRUE, FALSE, FALSE, TRUE, x, y) - -#ifdef GTK2 -# define GDKATOM_TO_ATOM(gdkAtom) gdk_x11_atom_to_xatom(gdkAtom) -#else -# define GDKATOM_TO_ATOM(gdkAtom) gdkAtom -#endif - -/* - * Forward Declarations - */ -static Bool DnDRpcInEnterCB (char const **result, size_t *resultLen, - const char *name, const char *args, - size_t argsSize,void *clientData); -static Bool DnDRpcInDataSetCB (char const **result, size_t *resultLen, - const char *name, const char *args, - size_t argsSize,void *clientData); -static Bool DnDRpcInMoveCB (char const **result, size_t *resultLen, - const char *name, const char *args, - size_t argsSize,void *clientData); -static Bool DnDRpcInDataFinishCB (char const **result, size_t *resultLen, - const char *name, const char *args, - size_t argsSize,void *clientData); -static Bool DnDRpcInDropCB (char const **result, size_t *resultLen, - const char *name, const char *args, - size_t argsSize,void *clientData); -static Bool DnDRpcInMouseUngrabCB(char const **result, size_t *resultLen, - const char *name, const char *args, - size_t argsSize,void *clientData); -static Bool DnDRpcInGetNextFileCB(char const **result, size_t *resultLen, - const char *name, const char *args, - size_t argsSize,void *clientData); -static Bool DnDRpcInFinishCB (char const **result, size_t *resultLen, - const char *name, const char *args, - size_t argsSize,void *clientData); - -/* - * Gtk DnD specific event/signal callbacks. - */ -/* For Host->Guest DnD */ -static void DnDGtkBeginCB(GtkWidget *widget, GdkDragContext *dc, gpointer data); -static void DnDGtkEndCB(GtkWidget *widget, GdkDragContext *dc, gpointer data); -static void DnDGtkDataRequestCB(GtkWidget *widget, GdkDragContext *dc, - GtkSelectionData *selection_data, - guint info, guint time, gpointer data); - -/* For Guest->Host DnD */ -static gboolean DnDGtkDragMotionCB(GtkWidget *widget, GdkDragContext *dc, gint x, - gint y, guint time, gpointer data); -static void DnDGtkDragDataReceivedCB(GtkWidget *widget, GdkDragContext *dc, - gint x, gint y, GtkSelectionData *dragData, - guint info, guint time, gpointer data); -static gboolean DnDGtkDragDropCB(GtkWidget *widget, GdkDragContext *dc, - gint x, gint y, guint time, gpointer data); - -/* - * Utility - */ -static Bool DnDSendVmxNewFileRoot(char *rpcCmd); -static Bool DnDFakeXEvents(GtkWidget *widget, Bool showWidget, - Bool buttonEvent, Bool buttonPress, - Bool moveWindow, - Bool coordsProvided, int x, int y); -static void DnDSendEscapeKey(GtkWidget *mainWnd); -static INLINE Bool DnDGHDragPending(GtkWidget *widget); -static INLINE Bool DnDGHXdndDragPending(GtkWidget *widget); -static INLINE void DnDGHXdndClearPending(GtkWidget *widget); -static INLINE Bool DnDGHMotifDragPending(GtkWidget *widget); -static INLINE void DnDGHFileListClear(void); -static INLINE void DnDGHFileListSet(char *fileList, size_t fileListSize); -static INLINE Bool DnDGHFileListGetNext(char **fileName, size_t *fileNameSize); -static INLINE void DnDGHStateInit(GtkWidget *widget); -static INLINE void DnDHGStateInit(void); -static INLINE Bool DnDGHCancel(GtkWidget *widget); -static Bool DnDGHXEventTimeout(void *clientData); - -/* - * Globals - */ -struct ghState { - Bool dragInProgress; - Bool ungrabReceived; - char *dndFileList; - char *dndFileListNext; - size_t dndFileListSize; - GdkDragContext *dragContext; - guint time; - Event *event; -} gGHState; -static Bool gHGDnDInProgress; -static Bool gDoneDragging; -static Bool gHGDataPending; -static char gDnDData[1024]; -static GdkDragContext *gDragCtx; -static GtkTargetEntry gTargetEntry[NR_DRAG_TARGETS]; -static GdkAtom gTargetEntryAtom[NR_GH_DRAG_TARGETS]; -static char gFileRoot[DND_MAX_PATH]; -static size_t gFileRootSize; -static size_t gDnDDataSize; -static Bool gUnity; - -/* - * From vmwareuserInt.h - */ -RpcIn *gRpcIn; -Display *gXDisplay; -Window gXRoot; - - -/* - * Host->Guest RPC callback implementations - */ - -/* - *----------------------------------------------------------------------------- - * - * DnDRpcInEnterCB -- - * - * For Host->Guest operations only. - * User has dragged something over this guest's MKS window - * - * Results: - * TRUE on success, FALSE otherwise - * - * Side effects: - * Some GdkEvents are generated which will "drag" the mouse. A directory - * is created. - * - *----------------------------------------------------------------------------- - */ - -static Bool -DnDRpcInEnterCB(char const **result, // OUT - size_t *resultLen, // OUT - const char *name, // IN - const char *args, // IN - size_t argsSize, // Ignored - void *clientData) // IN -{ - char *numFormats; - char *pFormat; - unsigned int index = 0; - int nFormats; - int i; - GtkWidget *mainWnd; - - Debug("Got DnDRpcInEnterCB\n"); - mainWnd = GTK_WIDGET(clientData); - if (mainWnd == NULL) { - return RpcIn_SetRetVals(result, resultLen, - "bad clientData passed to callback", FALSE); - } - - if (!DnD_BlockIsReady(&gBlockCtrl)) { - Debug("DnDRpcInEnterCB: cannot allow H->G DnD without vmblock.\n"); - return RpcIn_SetRetVals(result, resultLen, - "blocking file system unavailable", FALSE); - } - - numFormats = StrUtil_GetNextToken(&index, args, " "); - if (!numFormats) { - Debug("DnDRpcInEnterCB: Failed to parse numformats\n"); - return RpcIn_SetRetVals(result, resultLen, - "must specify number of formats", FALSE); - } - - /* Skip whitespace character. */ - index++; - - nFormats = atoi(numFormats); - free(numFormats); - - for (i = 0; i < nFormats; i++) { - pFormat = StrUtil_GetNextToken(&index, args, ","); - - if (!pFormat) { - Debug("DnDRpcInEnterCB: Failed to parse format list\n"); - return RpcIn_SetRetVals(result, resultLen, - "Failed to read format list", FALSE); - } else { - /* - * TODO: check that formats are ok for us to handle. For now, this is - * ok since there should only be a CF_HDROP. But, we really should figure - * out a much more cross-platform format scheme - */ - free(pFormat); - } - } - - if (!DnDHGFakeDrag(mainWnd)) { - Debug("DnDRpcInEnterCB: Failed to fake X events\n"); - return RpcIn_SetRetVals(result, resultLen, - "failed to fake drag", FALSE); - } - - RpcIn_SetRetVals(result, resultLen, "", TRUE); - RpcOut_sendOne(NULL, NULL, "dnd.feedback copy"); - Debug("DnDRpcInEnterCB finished\n"); - return TRUE; -} - - -/* - *----------------------------------------------------------------------------- - * - * DnDRpcInDataSetCB -- - * - * For Host->Guest operations only. - * Host is sending data from a DnD operation. - * - * Results: - * TRUE on success, FALSE otherwise. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -static Bool -DnDRpcInDataSetCB(char const **result, // OUT - size_t *resultLen, // OUT - const char *name, // IN - const char *args, // IN - size_t argsSize, // IN: Size of args - void *clientData) // Ignored -{ - char blockDir[DND_MAX_PATH]; - char *perDnDDir = NULL; - char *format; - char *data; - unsigned int index = 0; - size_t dataSize; - char *retStr; - Bool ret = FALSE; - - Debug("DnDRpcInDataSetCB: enter\n"); - - if (!DnD_BlockIsReady(&gBlockCtrl)) { - Debug("DnDRpcInDataSetCB: blocking file system not available.\n"); - return RpcIn_SetRetVals(result, resultLen, - "blocking file system not available", FALSE); - } - - /* Parse the data type & value string. */ - format = StrUtil_GetNextToken(&index, args, " "); - if (!format) { - Debug("DnDRpcInDataSetCB: Failed to parse format\n"); - return RpcIn_SetRetVals(result, resultLen, "need format", FALSE); - } - - index++; /* Ignore leading space before data. */ - dataSize = argsSize - index; - data = Util_SafeMalloc(dataSize); - memcpy(data, args + index, dataSize); - - Debug("DnDRpcInDataSetCB: Received data from host: (%s) [%s] (%"FMTSZ"u)\n", - format, CPName_Print(data, dataSize), dataSize); - - /* - * Here we take the last component of the actual file root, which is - * a temporary directory for this DnD operation, and append it to the mount - * point for vmblock. This is where we want the target application to - * access the file since it will enable vmblock to block that application's - * progress if necessary. - */ - perDnDDir = DnD_GetLastDirName(gFileRoot); - if (!perDnDDir) { - Debug("DnDRpcInDataSetCB: cannot obtain dirname of root.\n"); - retStr = "error obtaining dirname of root"; - goto out; - } - - if (strlen(gBlockCtrl.blockRoot) + - (sizeof DIRSEPS - 1) * 2 + strlen(perDnDDir) >= sizeof blockDir) { - Debug("DnDRpcInDataSetCB: blocking directory path too large.\n"); - retStr = "blocking directory path too large"; - goto out; - } - - Str_Sprintf(blockDir, sizeof blockDir, - "%s" DIRSEPS "%s" DIRSEPS, gBlockCtrl.blockRoot, perDnDDir); - - /* Add the file root to the relative paths received from host */ - if (!DnD_PrependFileRoot(blockDir, &data, &dataSize)) { - Debug("DnDRpcInDataSsetCB: error prepending guest file root\n"); - retStr = "error prepending file root"; - goto out; - } - if (dataSize + 1 > sizeof gDnDData) { - Debug("DnDRpcInDataSetCB: data too large\n"); - retStr = "data too large"; - goto out; - } - - memcpy(gDnDData, data, dataSize + 1); - gDnDDataSize = dataSize; - Debug("DnDRpcInDataSetCB: prepended file root [%s] (%"FMTSZ"u)\n", - CPName_Print(gDnDData, gDnDDataSize), gDnDDataSize); - - retStr = ""; - ret = TRUE; - -out: - free(format); - free(data); - free(perDnDDir); - return RpcIn_SetRetVals(result, resultLen, retStr, ret); -} - - -/* - *----------------------------------------------------------------------------- - * - * DnDRpcInMoveCB -- - * - * For Host->Guest operations only. - * Host user is dragging data over this guest's MKS window - * - * Results: - * TRUE on success, FALSE otherwise. - * - * Side effects: - * Send a gdk event that "moves" the mouse. - * - *----------------------------------------------------------------------------- - */ - -static Bool -DnDRpcInMoveCB(char const **result, // OUT - size_t *resultLen, // OUT - const char *name, // IN - const char *args, // IN - size_t argsSize, // Ignored - void *clientData) // IN: pointer to mainWnd -{ - GtkWidget *mainWnd; - char *sXCoord; - char *sYCoord; - unsigned int index = 0; - int xCoord, yCoord; - - mainWnd = GTK_WIDGET(clientData); - if (mainWnd == NULL) { - return RpcIn_SetRetVals(result, resultLen, - "bad clientData passed to callback", FALSE); - } - - sXCoord = StrUtil_GetNextToken(&index, args, " "); - sYCoord = StrUtil_GetNextToken(&index, args, " "); - - if (!sXCoord || !sYCoord) { - Debug("DnDRpcInMove: Failed to parse coords\n"); - free(sXCoord); - free(sYCoord); - return RpcIn_SetRetVals(result, resultLen, - "error reading mouse move data", FALSE); - } - - xCoord = atoi(sXCoord); - yCoord = atoi(sYCoord); - - free(sXCoord); - free(sYCoord); - - /* Fake a mouse move */ - if (!DnDHGFakeMove(mainWnd, xCoord, yCoord)) { - Debug("DnDRpcInMove: Failed to fake mouse movement\n"); - return RpcIn_SetRetVals(result, resultLen, - "failed to move mouse", FALSE); - } - - return RpcIn_SetRetVals(result, resultLen, "", TRUE); -} - - -/* - *----------------------------------------------------------------------------- - * - * DnDRpcInDataFinishCB -- - * - * For Host->Guest operations only. - * Host has finished transferring DnD data to the guest. We do any post - * H->G operation cleanup here, like removing the block on the staging - * directory, picking a new file root, and informing the host of the new - * root. - * - * Results: - * TRUE on success, FALSE otherwise - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -static Bool -DnDRpcInDataFinishCB(char const **result, // OUT - size_t *resultLen, // OUT - const char *name, // IN - const char *args, // IN - size_t argsSize, // Ignored - void *clientData) // Ignored -{ - unsigned int index = 0; - char *state; - - Debug("DnDRpcInDataFinishCB: enter\n"); - - state = StrUtil_GetNextToken(&index, args, " "); - if (!state) { - Debug("DnDRpcInDataFinishCB: could not get dnd finish state.\n"); - return RpcIn_SetRetVals(result, resultLen, - "could not get dnd finish state", FALSE); - } - - /* - * If the guest doesn't support vmblock, we'll have bailed out of - * DndRpcInDropCB before setting gHGDataPending. Thus, it doesn't make sense - * to pop a warning here, but let's keep the message around just in case - * there can be a failure worth hearing about. - */ - if (!gHGDataPending) { - Debug("DnDRpcInDataFinishCB: expected gHGDataPending to be set.\n"); - } - - gHGDataPending = FALSE; - - /* - * The host will send us "success" or "error", depending on whether the - * transfer finished successfully. In either case we remove the pending - * block, but in the "error" case we also need to delete all the files so - * the destination application doesn't access the partially copied files and - * mistake them for a successful drop. - */ - if (strcmp(state, "success") != 0) { - /* On any non-success input, delete the files. */ - DnD_DeleteStagingFiles(gFileRoot, FALSE); - } - - free(state); - - if (DnD_BlockIsReady(&gBlockCtrl) && - !gBlockCtrl.RemoveBlock(gBlockCtrl.fd, gFileRoot)) { - Warning("DnDRpcInDataFinishCB: could not remove block on %s\n", - gFileRoot); - } - - /* Pick a new file root and send that to the host for the next DnD. */ - if (!DnDSendVmxNewFileRoot("dnd.setGuestFileRoot")) { - Debug("DnDRpcInDataFinishCB: Failed to send dnd.setGuestFileRoot " - "message to host\n"); - return RpcIn_SetRetVals(result, resultLen, "could not send guest root", FALSE); - } - - return RpcIn_SetRetVals(result, resultLen, "", TRUE); -} - - -/* - *----------------------------------------------------------------------------- - * - * DnDRpcInDropCB -- - * - * For Host->Guest operations only. - * Host user has dropped data over this guest's MKS window. We add - * a block on the staging directory then send a fake mouse release to - * invoke the drop completion (from Gtk's point of view). - * - * Results: - * TRUE on success, FALSE otherwise. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -static Bool -DnDRpcInDropCB(char const **result, // OUT - size_t *resultLen, // OUT - const char *name, // IN - const char *args, // IN - size_t argsSize, // Ignored - void *clientData) // IN: Gtk window widget -{ - GtkWidget *mainWnd; - char *sXCoord; - char *sYCoord; - unsigned int index = 0; - int xCoord, yCoord; - - Debug("DnDRpcInDropCB: enter\n"); - - gDoneDragging = TRUE; - - mainWnd = GTK_WIDGET(clientData); - if (mainWnd == NULL) { - return RpcIn_SetRetVals(result, resultLen, - "bad clientData passed to callback", FALSE); - } - - sXCoord = StrUtil_GetNextToken(&index, args, " "); - sYCoord = StrUtil_GetNextToken(&index, args, " "); - - if (!sXCoord || !sYCoord) { - Debug("DnDRpcInDropCB: Failed to parse coords\n"); - free(sXCoord); - free(sYCoord); - return RpcIn_SetRetVals(result, resultLen, - "must specify drop coordinates", FALSE); - } - - xCoord = atoi(sXCoord); - yCoord = atoi(sYCoord); - - free(sXCoord); - free(sYCoord); - - Debug("DnDRpcInDropCB: Received drop notification at (%d,%d)\n", - xCoord, yCoord); - - /* - * Add a block on the guest file root, warp the pointer, then fake the mouse - * release. Make sure we'll succeed before modifying any mouse state in the - * guest. - */ - if (!DnD_BlockIsReady(&gBlockCtrl)) { - /* - * We shouldn't get here since DnDRpcInEnterCB() checks this, but we'll - * check rather than ASSERT just in case. - */ - return RpcIn_SetRetVals(result, resultLen, - "blocking file system unavailable", FALSE); - } - - if (!gBlockCtrl.AddBlock(gBlockCtrl.fd, gFileRoot)) { - return RpcIn_SetRetVals(result, resultLen, "could not add block", FALSE); - } - - /* Update state before causing faking any mouse or keyboard changes. */ - gHGDataPending = TRUE; - - - if (!DnDHGFakeDrop(mainWnd, xCoord, yCoord)) { - Debug("DnDRpcInDropCB: failed to fake drop\n"); - return RpcIn_SetRetVals(result, resultLen, "failed to fake drop", FALSE); - } - - return RpcIn_SetRetVals(result, resultLen, "", TRUE); -} - - -/* - * Guest->Host RPC callback implementations - */ - -/* - *---------------------------------------------------------------------------- - * - * DnDRpcInMouseUngrabCB -- - * - * For Guest->Host operations only. - * - * Called when a mouse ungrab is attempted with the mouse button down. When - * the MKS sees mouse movements outside of the clip (the viewable portion of - * the guest's display) while a mouse button is down, this function is - * called so we can inform the MKS whether to allow the ungrab (and start - * a DnD if one is pending). - * - * Results: - * TRUE on success, FALSE on failure. - * - * Side effects: - * The GDK window is moved and resized, and the mouse is moved over it. - * - *---------------------------------------------------------------------------- - */ - -static Bool -DnDRpcInMouseUngrabCB(char const **result, // OUT - size_t *resultLen, // OUT - const char *name, // IN - const char *args, // IN - size_t argsSize, // Ignored - void *clientData) // IN -{ - unsigned int index = 0; - GtkWidget *mainWnd; - int32 xPos; - int32 yPos; - - Debug("Got DnDRpcInMouseUngrabCB\n"); - mainWnd = GTK_WIDGET(clientData); - if (mainWnd == NULL) { - Warning("DnDRpcInMouseUngrabCB: invalid clientData\n"); - return RpcIn_SetRetVals(result, resultLen, - "bad clientData passed to callback", FALSE); - } - - /* - * If there is already a DnD or copy/paste in progress (including the file - * transfer), don't allow another. - */ - if (gHGDataPending || gGHState.dragInProgress || CopyPaste_InProgress()) { - RpcOut_sendOne(NULL, NULL, "dnd.notpending"); - return RpcIn_SetRetVals(result, resultLen, - "dnd already in progress", FALSE); - } - - if (!StrUtil_GetNextIntToken(&xPos, &index, args, " ")) { - Warning("DnDRpcInMouseUngrabCB: could not parse x coordinate\n"); - RpcOut_sendOne(NULL, NULL, "dnd.notpending"); - return RpcIn_SetRetVals(result, resultLen, - "Failed to parse x coordinate", FALSE); - } - - if (!StrUtil_GetNextIntToken(&yPos, &index, args, " ")) { - Warning("DnDRpcInMouseUngrabCB: could not parse y coordinate\n"); - RpcOut_sendOne(NULL, NULL, "dnd.notpending"); - return RpcIn_SetRetVals(result, resultLen, - "Failed to parse y coordinate", FALSE); - } - - Debug("DnDRpcInMouseUngrabCB: Received (%d,%d)\n", xPos, yPos); - - /* - * If there is no DnD pending, inform the host so the MKS can start sending - * mouse packets again. - */ - if (!DnDGHDragPending(mainWnd)) { - RpcOut_sendOne(NULL, NULL, "dnd.notpending"); - return RpcIn_SetRetVals(result, resultLen, "DnD not pending", TRUE); - } - - /* The host only gives us coordinates within our screen */ - ASSERT(xPos >= 0); - ASSERT(yPos >= 0); - - /* - * Fake mouse movements over the window to try and generate a "drag_motion" - * signal from GTK. If a drag is pending, that signal will be sent to our - * widget and DnDGtkDragMotionCB will be invoked to start the DnD - * operation. - */ - if (!DnDGHFakeDrag(mainWnd)) { - Warning("DnDRpcInMouseUngrabCB: could not fake X events\n"); - RpcOut_sendOne(NULL, NULL, "dnd.notpending"); - return RpcIn_SetRetVals(result, resultLen, - "error faking X events", FALSE); - } - - /* - * Add event to fire and hide our widget if a DnD is not pending. Note that - * this is here in case our drag pending heuristic for Xdnd and Motif does - * not encompass all cases, or if the X events we generate don't cause the - * "drag_motion" for some other reason. - */ - gGHState.event = EventManager_Add(gEventQueue, RPCIN_POLL_TIME * 100, - DnDGHXEventTimeout, mainWnd); - if (!gGHState.event) { - Warning("DnDRpcInMouseUngrabCB: could not create event\n"); - RpcOut_sendOne(NULL, NULL, "dnd.notpending"); - return RpcIn_SetRetVals(result, resultLen, - "could not create timeout event", FALSE); - } - - gGHState.dragInProgress = FALSE; - gGHState.ungrabReceived = TRUE; - - Debug("DnDRpcInMouseUngrabCB finished\n"); - return RpcIn_SetRetVals(result, resultLen, "", TRUE); -} - - -/* - *---------------------------------------------------------------------------- - * - * DnDRpcInGetNextFileCB -- - * - * For Guest->Host operations only. - * - * Invoked when the host is compiling its list of files to copy from the - * guest. Here we provide the path of the next file in our Guest->Host file - * list in guest path format (for display purposes) and CPName format (for - * file copy operation). - * - * Results: - * TRUE on success, FALSE on failure. - * - * Side effects: - * Iterator pointer within file list of GH state is iterated to next list - * entry (through call to DnDGHFileListGetNext()). - * - *---------------------------------------------------------------------------- - */ - -static Bool -DnDRpcInGetNextFileCB(char const **result, // OUT - size_t *resultLen, // OUT - const char *name, // IN - const char *args, // IN - size_t argsSize, // Ignored - void *clientData) // IN -{ - static char resultBuffer[DND_MAX_PATH]; - char *fileName; - size_t fileNameSize; - uint32 cpNameSize; - GtkWidget *mainWnd; - Bool res; - - mainWnd = GTK_WIDGET(clientData); - if (mainWnd == NULL) { - DnDGHCancel(NULL); - return RpcIn_SetRetVals(result, resultLen, - "bad clientData passed to callback", FALSE); - } - - /* - * Retrieve a pointer to the next filename and its size from the list stored - * in the G->H DnD state. Note that fileName should not be free(3)d here - * since an additional copy is not allocated. - */ - res = DnDGHFileListGetNext(&fileName, &fileNameSize); - - if (!res) { - Warning("DnDRpcInGetNextFileCB: error retrieving file name\n"); - DnDGHCancel(mainWnd); - return RpcIn_SetRetVals(result, resultLen, "error getting file", FALSE); - } - - if (!fileName) { - /* There are no more files to send */ - Debug("DnDRpcInGetNextFileCB: reached end of Guest->Host file list\n"); - return RpcIn_SetRetVals(result, resultLen, "|end|", TRUE); - } - - if (fileNameSize + 1 + fileNameSize > sizeof resultBuffer) { - Warning("DnDRpcInGetNextFileCB: filename too large (%"FMTSZ"u)\n", fileNameSize); - DnDGHCancel(mainWnd); - return RpcIn_SetRetVals(result, resultLen, "filename too large", FALSE); - } - - /* - * Construct a reply message of the form: - * - */ - memcpy(resultBuffer, fileName, fileNameSize); - resultBuffer[fileNameSize] = '\0'; - - cpNameSize = CPNameUtil_ConvertToRoot(fileName, - sizeof resultBuffer - (fileNameSize + 1), - resultBuffer + fileNameSize + 1); - if (cpNameSize < 0) { - Warning("DnDRpcInGetNextFileCB: could not convert to CPName\n"); - DnDGHCancel(mainWnd); - return RpcIn_SetRetVals(result, resultLen, - "error on CPName conversion", FALSE); - } - - /* Set manually because RpcIn_SetRetVals() assumes no NUL characters */ - *result = resultBuffer; - *resultLen = fileNameSize + 1 + cpNameSize; - - Debug("DnDRpcInGetNextFileCB: [%s] (%"FMTSZ"u)\n", - CPName_Print(*result, *resultLen), *resultLen); - - return TRUE; -} - - -/* - *---------------------------------------------------------------------------- - * - * DnDRpcInFinishCB -- - * - * For Guest->Host operations only. - * - * Invoked when host side of DnD operation has finished. - * - * Results: - * TRUE on success, FALSE on failure. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static Bool -DnDRpcInFinishCB(char const **result, // OUT - size_t *resultLen, // OUT - const char *name, // IN - const char *args, // IN - size_t argsSize, // Ignored - void *clientData) // IN -{ - GtkWidget *mainWnd; - char *effect = NULL; - unsigned int index = 0; - char *retStr; - Bool ret = FALSE; - - mainWnd = GTK_WIDGET(clientData); - if (mainWnd == NULL) { - retStr = "bad clientData passed to callback"; - goto exit; - } - - effect = StrUtil_GetNextToken(&index, args, " "); - if (!effect) { - Warning("DnDRpcInFinishCB: no drop effect provided\n"); - retStr = "drop effect not provided"; - goto exit; - } - - if (strcmp(effect, "cancel") == 0) { - DnDSendEscapeKey(mainWnd); - DnDGHCancel(mainWnd); - } else { - /* - * The drop happened on the host. Fake X events such that our window is - * placed at the mouse's coordinates and raised, then fake a button - * release on the window. This causes us to get a "drag_drop" signal - * from GTK on our widget. - */ - if (!DnDGHFakeDrop(mainWnd)) { - Warning("DnDRpcInFinishCB: could not fake X events\n"); - retStr = "error faking X events"; - goto exit; - } - - gGHState.event = EventManager_Add(gEventQueue, RPCIN_POLL_TIME * 10, - DnDGHXEventTimeout, mainWnd); - if (!gGHState.event) { - Warning("DnDRpcInFinishCB: could not create event\n"); - retStr = "could not create timeout event"; - goto exit; - } - } - - retStr = ""; - ret = TRUE; - -exit: - if (!ret) { - DnDGHCancel(mainWnd); - } - - free(effect); - gGHState.dragInProgress = FALSE; - return RpcIn_SetRetVals(result, resultLen, retStr, ret); -} - - -/* - * Host->Guest (drop source) Gtk callback implementations - */ - -/* - *----------------------------------------------------------------------------- - * - * DnDGtkBeginCB -- - * - * "drag_begin" signal handler for GTK. This signal will be received - * after the fake mouse press sent in DnDRpcInEnterCB() is performed. - * Here we simply initialize our state variables. - * - * Results: - * None - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -static void -DnDGtkBeginCB(GtkWidget *widget, // IN: the widget under the drag - GdkDragContext *dc, // IN: the drag context maintained by gdk - gpointer data) // IN: unused -{ - GtkWidget *mainWnd = GTK_WIDGET(data); - - Debug("DnDGtkBeginCB: entry\n"); - - if ((widget == NULL) || (mainWnd == NULL) || (dc == NULL)) { - return; - } - - gHGDnDInProgress = TRUE; - gDoneDragging = FALSE; - gHGDataPending = FALSE; -} - - -/* - *----------------------------------------------------------------------------- - * - * DnDGtkEndCB -- - * - * "drag_end" signal handler for GTK. This is called when a drag and drop has - * completed. So this function is the last one to be called in any given DnD - * operation. - * - * Results: - * None - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -static void -DnDGtkEndCB(GtkWidget *widget, // IN: the widget under the drag - GdkDragContext *dc, // IN: the drag context maintained by gdk - gpointer data) // IN: unused -{ - - GtkWidget *mainWnd = GTK_WIDGET(data); - - Debug("DnDGtkEndCB: enter\n"); - - if (mainWnd == NULL || dc == NULL) { - return; - } - - /* - * Do not set gHGDataPending to FALSE since DnD operation completes before - * the data transfer. - */ - gDoneDragging = FALSE; - gHGDnDInProgress = FALSE; - - RpcOut_sendOne(NULL, NULL, "dnd.finish %d", DROPEFFECT_COPY); -} - - -/* - *----------------------------------------------------------------------------- - * - * DnDGtkDataRequestCB -- - * DnD "drag_data_get" handler, for handling requests for DnD data on the - * specified widget. This function is called when there is need for DnD data - * on thesource, so this function is responsible for setting up the dynamic - * data exchange buffer and sending it out. - * - * Results: - * None - * - * Side effects: - * Data is avaiable to drop target. - * - *----------------------------------------------------------------------------- - */ - -static void -DnDGtkDataRequestCB(GtkWidget *widget, // IN - GdkDragContext *dc, // IN - GtkSelectionData *selection_data, // IN/OUT: buffer for the data - guint info, // IN: the requested fo the data - guint time, // IN: unused - gpointer data) // IN: unused -{ - const char *begin; - const char *end; - const char *next; - const char *pre; - const char *post; - size_t preLen; - size_t postLen; - int len; - Bool insertSpace; - char *text = NULL; - size_t textLen = 1; - - Debug("DnDGtkDataRequestCB: enter\n"); - - if ((widget == NULL) || (dc == NULL) || (selection_data == NULL)) { - Debug("DnDGtkDataRequestCB: Error, widget or dc or selection_data is invalid\n"); - return; - } - - /* Do nothing if we have not finished dragging yet */ - if (!gDoneDragging) { - Debug("DnDGtkDataRequestCB: not done dragging yet\n"); - return; - } - - /* Setup the format string components */ - switch (info) { - case DRAG_TARGET_INFO_URI_LIST: /* text/uri-list */ - pre = DND_URI_LIST_PRE; - preLen = sizeof DND_URI_LIST_PRE - 1; - post = DND_URI_LIST_POST; - postLen = sizeof DND_URI_LIST_POST - 1; - insertSpace = FALSE; - break; - case DRAG_TARGET_INFO_TEXT_PLAIN: /* text/plain */ - pre = DND_TEXT_PLAIN_PRE; - preLen = sizeof DND_TEXT_PLAIN_PRE - 1; - post = DND_TEXT_PLAIN_POST; - postLen = sizeof DND_TEXT_PLAIN_POST - 1; - insertSpace = TRUE; - break; - case DRAG_TARGET_INFO_STRING: /* STRING */ - pre = DND_STRING_PRE; - preLen = sizeof DND_STRING_PRE - 1; - post = DND_STRING_POST; - postLen = sizeof DND_STRING_POST - 1; - insertSpace = TRUE; - break; - default: - Log("DnDGtkDataRequestCB: invalid drag target info\n"); - return; - } - - - /* - * Set begin to first non-NUL character and end to last NUL character to - * prevent errors in calling CPName_GetComponent(). - */ - for(begin = gDnDData; *begin == '\0'; begin++) - ; - end = CPNameUtil_Strrchr(gDnDData, gDnDDataSize + 1, '\0'); - ASSERT(end); - - /* Build up selection data */ - while ((len = CPName_GetComponent(begin, end, &next)) != 0) { - const size_t origTextLen = textLen; - Bool freeBegin = FALSE; - - if (len < 0) { - Log("DnDGtkDataRequestCB: error getting next component\n"); - if (text) { - free(text); - } - return; - } - - /* - * A URI list will expect the provided path to be escaped. If we cannot - * escape the path for some reason we just use the unescaped version and - * hope that it works. - */ - if (info == DRAG_TARGET_INFO_URI_LIST) { - size_t newLen; - char *escapedComponent; - int escIndex; - int bytesToEsc[256] = { 0, }; - - /* We escape the following characters based on RFC 1630. */ - bytesToEsc['#'] = 1; - bytesToEsc['?'] = 1; - bytesToEsc['*'] = 1; - bytesToEsc['!'] = 1; - bytesToEsc['%'] = 1; /* Escape character */ - - /* Escape non-ASCII characters so we can pass UTF-8 filenames */ - for (escIndex = 0x80; escIndex < 0x100; escIndex++) { - bytesToEsc[escIndex] = 1; - } - - escapedComponent = Escape_Do('%', bytesToEsc, begin, len, &newLen); - if (escapedComponent) { - begin = escapedComponent; - len = newLen; - freeBegin = TRUE; - } - } - - /* - * Append component. NUL terminator was accounted for by initializing - * textLen to one above. - */ - textLen += preLen + len + postLen + (insertSpace ? 1 : 0); - text = Util_SafeRealloc(text, textLen); - Str_Snprintf(text + origTextLen - 1, - textLen - origTextLen + 1, - "%s%s%s", pre, begin, post); - - if (insertSpace && next != end) { - ASSERT(textLen - 2 >= 0); - text[textLen - 2] = ' '; - text[textLen - 1] = '\0'; - } - - if (freeBegin) { - free((void *)begin); - } - - /* Iterate to next component */ - begin = next; - } - - /* - * Send out the data using the selection system. When sending a string, GTK will - * ensure that a null terminating byte is added to the end so we do not need to - * add it. GTK also copies the data so the original will never be modified. - */ - Debug("DnDGtkDataRequestCB: calling gtk_selection_data_set with [%s]\n", text); - gtk_selection_data_set(selection_data, selection_data->target, - 8, /* 8 bits per character. */ - text, textLen); - free(text); -} - - -/* - * Guest->Host (drop target) Gtk callback implementations - */ - -/* - *---------------------------------------------------------------------------- - * - * DnDGtkDragMotionCB -- - * - * "drag_motion" signal handler for GTK. This is invoked each time the - * mouse moves over the drag target (destination) window when a DnD is - * pending. - * - * Results: - * TRUE on success, FALSE on failure. - * - * Side effects: - * RPC messages are sent to the host to proxy the DnD over. - * - *---------------------------------------------------------------------------- - */ - -static gboolean -DnDGtkDragMotionCB(GtkWidget *widget, // IN: target widget - GdkDragContext *dc, // IN: the GDK drag context - gint x, // IN: x position of mouse - gint y, // IN: y position of mouse - guint time, // IN: time of event - gpointer data) // IN: our private data -{ - GdkAtom commonTarget = 0; - Bool found = FALSE; - uint32 i; - - ASSERT(widget); - ASSERT(widget == data); - ASSERT(dc); - - Debug("DnDGtkDragMotionCB: entry (x=%d, y=%d, time=%d)\n", x, y, time); - - /* - * We'll get a number of these and should only carry on these operations on - * the first one. - * - * XXX Unity mode needs to know if there is a g->h->g dnd operation by - * detecting if the mouse has left the detection window. This code is - * currently not in the guest and should be ported from the host. - */ - if (gGHState.dragInProgress && !gUnity) { - Debug("DnDGtkDragMotionCB: drag already in progress\n"); - return FALSE; - } - - /* - * Sometimes (rarely) real user mouse movements will trigger "drag_motion" - * signals after we have already handled them. Prevent resetting the data - * and trying to start a new DnD operation. - */ - if (!gGHState.ungrabReceived && !gUnity) { - Debug("DnDGtkDragMotionCB: extra drag motion without ungrab\n"); - return FALSE; - } - - gGHState.ungrabReceived = FALSE; - - /* Remove event that hides our widget out of band from the DnD protocol. */ - if (gGHState.event) { - Debug("DnDGtkDragMotionCB: removed pending event\n"); - EventManager_Remove(gGHState.event); - gGHState.event = NULL; - } - - /* - * Note that gdk_drag_status() is called for us by GTK since we passed in - * GTK_DEST_DEFAULT_MOTION to gtk_drag_dest_set(). We'd handle it - * ourselves, but GTK 1.2.10 has a "bug" that requires us to provide this - * flag to get drag_leave and drag_drop signals. - */ - - /* - * We need to try and find a common target format with the list of formats - * offered by the drag source. This list is stored in the drag context's - * targets field, and each list member's data variable is a GdkAtom. We - * translated our supported targets into GdkAtoms in gTargetEntryAtom at - * initialization. Note that the GdkAtom value is an index into a table of - * strings maintained by the X server, so if they are equivalent then - * a common mime type is found. - */ - for (i = 0; i < ARRAYSIZE(gTargetEntryAtom) && !found; i++) { - GList *currContextTarget = dc->targets; - - while (currContextTarget) { - if (gTargetEntryAtom[i] == (GdkAtom)currContextTarget->data) { - commonTarget = gTargetEntryAtom[i]; - found = TRUE; - break; - } - currContextTarget = currContextTarget->next; - } - } - - if (!found) { - Warning("DnDGtkDragMotionCB: could not find a common target format\n"); - DnDGHCancel(widget); - return FALSE; - } - - /* - * Request the data. A "drag_data_received" signal will be sent to widget - * (that's us) upon completion. - */ - gtk_drag_get_data(widget, dc, commonTarget, time); - - - gGHState.dragInProgress = TRUE; - return TRUE; -} - - -/* - *---------------------------------------------------------------------------- - * - * DnDGtkDragDataReceivedCB -- - * - * "drag_data_received" signal handler for GTK. Invoked when the data - * requested by a gtk_drag_get_data() call is ready. - * - * This function actually begins the drag operation with the host by first - * setting the data ("dnd.data.set" RPC command) and then starting the DnD - * ("dnd.enter" RPC command). - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static void -DnDGtkDragDataReceivedCB(GtkWidget *widget, // IN - GdkDragContext *dc, // IN - gint x, // IN - gint y, // IN - GtkSelectionData *dragData, // IN - guint info, // IN - guint time, // IN - gpointer data) // IN -{ - const char rpcHeader[] = "dnd.data.set CF_HDROP "; - const size_t rpcHeaderSize = sizeof rpcHeader - 1; - char *rpcBody = NULL; - size_t rpcBodySize = 0; - char *rpc; - size_t rpcSize; - - - Debug("DnDGtkDragDataReceivedCB: entry\n"); - - if (dragData->length < 0) { - Warning("DnDGtkDragDataReceivedCB: received length < 0 error\n"); - goto error; - } - - gGHState.dragContext = dc; - gGHState.time = time; - - /* - * Construct the body of the RPC message and our Guest->Host file list. - */ - if (dragData->target == gTargetEntryAtom[DRAG_TARGET_INFO_URI_LIST]) { - char *currName; - size_t currSize; - size_t index = 0; - char *ghFileList = NULL; - size_t ghFileListSize = 0; - - Debug("DnDGtkDragDataReceivedCB: uri-list [%s]\n", dragData->data); - - /* - * Get the the full filenames and last components from the URI list. The - * body of the RPC message will be these last components delimited with - * NUL characters; the Guest->Host file list will be the full paths - * delimited by NUL characters. - */ - while ((currName = DnD_UriListGetNextFile(dragData->data, - &index, - &currSize))) { - size_t lastComponentSize; - char *lastComponentStart; - - /* Append current filename to Guest->Host list */ - ghFileList = Util_SafeRealloc(ghFileList, - ghFileListSize + currSize + 1); - memcpy(ghFileList + ghFileListSize, currName, currSize); - ghFileListSize += currSize; - ghFileList[ghFileListSize] = '\0'; - ghFileListSize++; - - /* Append last component to RPC body */ - lastComponentStart = CPNameUtil_Strrchr(currName, currSize, DIRSEPC); - if (!lastComponentStart) { - /* - * This shouldn't happen since filenames are absolute, but handle - * it as if the file name is the last component - */ - lastComponentStart = currName; - } else { - /* Skip the last directory separator */ - lastComponentStart++; - } - - lastComponentSize = currName + currSize - lastComponentStart; - rpcBody = Util_SafeRealloc(rpcBody, rpcBodySize + lastComponentSize + 1); - memcpy(rpcBody + rpcBodySize, lastComponentStart, lastComponentSize); - rpcBodySize += lastComponentSize; - rpcBody[rpcBodySize] = '\0'; - rpcBodySize++; - - free(currName); - } - - if (!ghFileList || !rpcBody) { - Warning("DnDGtkDragDataReceivedCB: no filenames retrieved " - "from URI list\n"); - free(ghFileList); - free(rpcBody); - goto error; - } - - /* Set the list of full paths for use in the "dnd.data.get.file" callback */ - DnDGHFileListSet(ghFileList, ghFileListSize); - - /* rpcBody (and its size) will always contain a trailing NUL character */ - rpcBodySize--; - } else { - Warning("DnDGtkDragDataReceivedCB: unknown target format used [%s]\n", - dragData->data); - goto error; - } - - /* - * Set the drag data on the host, followed by sending the drag enter - */ - rpcSize = rpcHeaderSize + rpcBodySize; - rpc = Util_SafeMalloc(rpcSize); - memcpy(rpc, rpcHeader, rpcHeaderSize); - memcpy(rpc + rpcHeaderSize, rpcBody, rpcBodySize); - free(rpcBody); - - Debug("DnDGtkDragMotionCB: Sending: [%s] (%"FMTSZ"u)\n", - CPName_Print(rpc, rpcSize), rpcSize); - if (!RpcOut_SendOneRaw(rpc, rpcSize, NULL, NULL)) { - Warning("DnDGtkDragMotionCB: failed to send dnd.data.set message\n"); - free(rpc); - goto error; - } - - free(rpc); - - if (!RpcOut_sendOne(NULL, NULL, "dnd.enter 1 CF_HDROP")) { - Warning("DnDGtkDragMotionCB: failed to send dnd.enter message\n"); - goto error; - } - - return; - -error: - RpcOut_sendOne(NULL, NULL, "dnd.notpending"); - DnDGHCancel(widget); -} - - -/* - *---------------------------------------------------------------------------- - * - * DnDGtkDragDropCB -- - * - * "drag_drop" signal handler for GTK. This is invoked when a mouse button - * release occurs on our widget. We generate that mouse button release in - * DnDRpcInFinishCB() when the host indicates that the drop has occurred and - * the files have been successfully transferred to the guest. - * - * Results: - * TRUE indicates to GTK that it need not run other handlers, FALSE - * otherwise. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static gboolean -DnDGtkDragDropCB(GtkWidget *widget, // IN: widget event occurred on - GdkDragContext *dc, // IN: Destination drag context - gint x, // IN: x coordinate of drop - gint y, // IN: y coordinate of drop - guint time, // IN: time of event - gpointer data) // IN: our private data -{ - ASSERT(widget); - ASSERT(widget == data); - - Debug("DnDGtkDragDropCB: entry (%d, %d)\n", x, y); - - /* Remove timeout callback that was set in case we didn't get here */ - if (gGHState.event) { - Debug("DnDGtkDragDropCB: removed pending event\n"); - EventManager_Remove(gGHState.event); - gGHState.event = NULL; - } - - /* Hide our window so we don't receive stray signals */ - if (!gUnity) { - gtk_widget_hide(widget); - } - - gtk_drag_finish(dc, TRUE, FALSE, time); - - /* Reset all Guest->Host state */ - DnDGHStateInit(widget); - - return FALSE; -} - - -/* - * Utility functions - */ - -/* - *---------------------------------------------------------------------------- - * - * DnD_GetNewFileRoot -- - * - * Convenience function that gets a new file root for use on a single DnD - * operation and sets the global file root variable accordingly. - * - * Results: - * Size of root string (not including NUL terminator). - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -DnD_GetNewFileRoot(char *fileRoot, // IN/OUT - int bufSize) // IN: sizeof fileRoot -{ - char *newDir = NULL; - size_t fileRootSize; - - newDir = DnD_CreateStagingDirectory(); - if (newDir == NULL) { - /* - * Fallback on base of file root if we couldn't create a staging - * directory for this DnD operation. This is what Windows DnD does. - */ - Str_Strcpy(fileRoot, DnD_GetFileRoot(), bufSize); - return strlen(fileRoot); - } else { - fileRootSize = strlen(newDir); - ASSERT(fileRootSize < bufSize); - memcpy(fileRoot, newDir, fileRootSize); - fileRoot[fileRootSize] = '\0'; - free(newDir); - return fileRootSize; - } -} - - -/* - *---------------------------------------------------------------------------- - * - * DnDSendVmxNewFileRoot -- - * - * Sends the VMX a new file root with the provided RPC command. - * - * Results: - * TRUE on success, FALSE on failure - * - * Side effects: - * gFileRoot is repopulated. - * - *---------------------------------------------------------------------------- - */ - -static Bool -DnDSendVmxNewFileRoot(char *rpcCmd) // IN: RPC command -{ - int32 rpcCommandSize; - int32 cpNameSize; - int32 rpcMessageSize; - char *rpcMessage; - char *cur; - - /* Repopulate gFileRoot */ - gFileRootSize = DnD_GetNewFileRoot(gFileRoot, sizeof gFileRoot); - - /* - * Here we must convert the file root before sending it across the - * backdoor. We can only communicate with new VMXs (v2 DnD), so we only - * need to handle that case here. - * - * - */ - rpcCommandSize = strlen(rpcCmd); - - /* - * ConvertToRoot below will append the root share name, so we need to - * make room for it in our buffer. - */ - rpcMessageSize = rpcCommandSize + 1 + - gFileRootSize + 1 + - HGFS_STR_LEN(HGFS_SERVER_POLICY_ROOT_SHARE_NAME) + 1 + - gFileRootSize + 1; - - rpcMessage = Util_SafeCalloc(1, rpcMessageSize); - memcpy(rpcMessage, rpcCmd, rpcCommandSize); - rpcMessage[rpcCommandSize] = ' '; - cur = rpcMessage + rpcCommandSize + 1; - - memcpy(cur, gFileRoot, gFileRootSize); - cur += gFileRootSize; - *cur = '\0'; - cur++; - - Debug("DnDSendVmxNewFileRoot: calling CPNameUtil_ConvertToRoot(%s, %"FMTSZ"u, %p)\n", - gFileRoot, rpcMessageSize - (cur - rpcMessage), cur); - cpNameSize = CPNameUtil_ConvertToRoot(gFileRoot, - rpcMessageSize - (cur - rpcMessage), - cur); - if (cpNameSize < 0) { - Debug("DnDSendVmxNewFileRoot: Could not convert file root to CPName\n"); - free(rpcMessage); - return FALSE; - } - - /* Readjust message size for actual length */ - rpcMessageSize = rpcCommandSize + 1 + - gFileRootSize + 1 + - cpNameSize + 1; - - Debug("DnDSendVmxNewFileRoot: sending root [%s] (%d)\n", - CPName_Print(rpcMessage, rpcMessageSize), rpcMessageSize); - - /* - * We must use RpcOut_SendOneRaw() here since RpcOut_sendOne() assumes a - * string and we are using CPName format. - */ - if (!RpcOut_SendOneRaw(rpcMessage, rpcMessageSize, NULL, NULL)) { - Debug("DnDSendVmxNewFileRoot: Failed to send %s message to host\n", rpcCmd); - free(rpcMessage); - return FALSE; - } - - free(rpcMessage); - return TRUE; -} - - -/* - *---------------------------------------------------------------------------- - * - * DnDFakeXEvents -- - * - * Fake X mouse events and window movement for the provided Gtk widget. - * - * This function will optionally show the widget, move the provided widget - * to either the provided location or the current mouse position if no - * coordinates are provided, and cause a button press or release event. - * - * - * Results: - * TRUE on success, FALSE on failure. - * - * Side effects: - * Other X events should be generated from those faked here. - * - *---------------------------------------------------------------------------- - */ - -static Bool -DnDFakeXEvents(GtkWidget *widget, // IN: the Gtk widget - Bool showWidget, // IN: whether to show Gtk widget - Bool buttonEvent, // IN: whether to send a button event - Bool buttonPress, // IN: whether to press or release mouse - Bool moveWindow, // IN: whether to move our window too - Bool coordsProvided,// IN: whether coordinates provided - int xCoord, // IN: x coordinate - int yCoord) // IN: y coordinate -{ - Window rootWnd; - Bool ret; - Display *dndXDisplay; - Window dndXWindow; - - ASSERT(widget); - - dndXDisplay = GDK_WINDOW_XDISPLAY(widget->window); - dndXWindow = GDK_WINDOW_XWINDOW(widget->window); - - /* - * Turn on X synchronization in order to ensure that our X events occur in - * the order called. In particular, we want the window movement to occur - * before the mouse movement so that the events we are coercing do in fact - * happen. - */ - XSynchronize(dndXDisplay, True); - - if (showWidget) { - Debug("DnDFakeXEvents: showing Gtk widget\n"); - gtk_widget_show(widget); - gdk_window_show(widget->window); - } - - /* Get the current location of the mouse if coordinates weren't provided. */ - if (!coordsProvided) { - Window rootReturn; - Window childReturn; - int rootXReturn; - int rootYReturn; - int winXReturn; - int winYReturn; - unsigned int maskReturn; - - rootWnd = RootWindow(dndXDisplay, DefaultScreen(dndXDisplay)); - ret = XQueryPointer(dndXDisplay, rootWnd, &rootReturn, &childReturn, - &rootXReturn, &rootYReturn, &winXReturn, &winYReturn, - &maskReturn); - if (ret == False) { - Warning("DnDFakeXEvents: XQueryPointer() returned False.\n"); - XSynchronize(dndXDisplay, False); - return FALSE; - } - - Debug("DnDFakeXEvents: mouse is at (%d, %d)\n", rootXReturn, rootYReturn); - - xCoord = rootXReturn; - yCoord = rootYReturn; - } - - if (moveWindow) { - /* - * Make sure the window is at this point and at the top (raised). The - * window is resized to be a bit larger than we would like to increase - * the likelihood that mouse events are attributed to our window -- this - * is okay since the window is invisible and hidden on cancels and DnD - * finish. - */ - XMoveResizeWindow(dndXDisplay, dndXWindow, xCoord, yCoord, 25, 25); - XRaiseWindow(dndXDisplay, dndXWindow); - } - - /* - * Generate mouse movements over the window. The second one makes ungrabs - * happen more reliably on KDE, but isn't necessary on GNOME. - */ - XTestFakeMotionEvent(dndXDisplay, -1, xCoord, yCoord, CurrentTime); - XTestFakeMotionEvent(dndXDisplay, -1, xCoord + 1, yCoord + 1, CurrentTime); - - if (buttonEvent) { - Debug("DnDFakeXEvents: faking left mouse button %s\n", - buttonPress ? "press" : "release"); - XTestFakeButtonEvent(dndXDisplay, 1, buttonPress, CurrentTime); - } - - XSynchronize(dndXDisplay, False); - - return TRUE; -} - - -/* - *---------------------------------------------------------------------------- - * - * DnDSendEscapeKey -- - * - * Sends the escape key, canceling any pending drag and drop on the guest. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static void -DnDSendEscapeKey(GtkWidget *mainWnd) // IN -{ - Display *dndXDisplay; - uint32 escKeycode; - - Debug("DnDRpcInFinishCB: faking ESC key press/release\n"); - - dndXDisplay = GDK_WINDOW_XDISPLAY(mainWnd->window); - escKeycode = XKeysymToKeycode(dndXDisplay, XK_Escape); - - XTestFakeKeyEvent(dndXDisplay, escKeycode, TRUE, CurrentTime); - XTestFakeKeyEvent(dndXDisplay, escKeycode, FALSE, CurrentTime); -} - - -/* - *---------------------------------------------------------------------------- - * - * DnDGHDragPending -- - * - * Determine whether a drag is currently pending within the guest by - * inspecting the internal state of the X server. Note that Gtk supports - * both the Xdnd and Motif protocols, so we check each one of those. - * - * Results: - * TRUE if a Drag operation is pending (waiting for a drop), FALSE - * otherwise. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static INLINE Bool -DnDGHDragPending(GtkWidget *widget) // IN: our widget -{ - /* Xdnd is much more prevalent, so call it first */ - return DnDGHXdndDragPending(widget) || DnDGHMotifDragPending(widget); -} - - -/* - *---------------------------------------------------------------------------- - * - * DnDGHXdndDragPending -- - * - * Determines whether an Xdnd protocol drag is pending. - * - * Results: - * TRUE is a drag is pending, FALSE otherwise. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static INLINE Bool -DnDGHXdndDragPending(GtkWidget *widget) // IN: our widget -{ - GdkAtom xDnDSelection; - Window owner; - - xDnDSelection = gdk_atom_intern("XdndSelection", TRUE); - if (xDnDSelection == None) { - Warning("DnDGHXdndDragPending: could not obtain Xdnd selection atom\n"); - return FALSE; - } - - /* - * The XdndSelection atom will only have an owner if there is a drag in - * progress. - */ - owner = XGetSelectionOwner(GDK_WINDOW_XDISPLAY(widget->window), - GDKATOM_TO_ATOM(xDnDSelection)); - - Debug("DnDGHXdndDragPending: an Xdnd drag is %spending\n", - owner != None ? "" : "not "); - - return owner != None; -} - - -/* - *---------------------------------------------------------------------------- - * - * DnDGHXdndClearPending -- - * - * Clear the ownership of the XdndSelection selection atom that we use to - * determine if a Xdnd drag is pending. - * - * Note that this function should only be called when a DnD is not in - * progress. - * - * Also note that this is function is only necessary to handle desktop - * environments that don't clear the selection owner themselves (read KDE). - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static INLINE void -DnDGHXdndClearPending(GtkWidget *widget) // IN: program's widget -{ - GdkAtom xDnDSelection; - - ASSERT(!gGHState.dragInProgress); - - xDnDSelection = gdk_atom_intern("XdndSelection", TRUE); - if (xDnDSelection == None) { - return; - } - - /* Clear current owner by setting owner to None */ - XSetSelectionOwner(GDK_WINDOW_XDISPLAY(widget->window), - GDKATOM_TO_ATOM(xDnDSelection), None, CurrentTime); -} - - -/* - *---------------------------------------------------------------------------- - * - * DnDMotifDragPending -- - * - * Determines whether a Motif protocol drag is pending. - * - * XXX This has not yet been tested (looking for an app that actually uses - * the Motif protocol) - * - * Results: - * TRUE if a drag is pending, FALSE otherwise. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static INLINE Bool -DnDGHMotifDragPending(GtkWidget *widget) // IN: our widget -{ - GdkAtom motifDragWindow; - Display *dndXDisplay; - int ret; - Window rootXWindow; - Atom type; - int format; - unsigned long nitems; - unsigned long bytesAfter; - unsigned char *prop; - - motifDragWindow = gdk_atom_intern("_MOTIF_DRAG_WINDOW", TRUE); - if (motifDragWindow == None) { - Warning("DnDGHMotifDragPending: could not obtain Motif " - "drag window atom\n"); - return FALSE; - } - - dndXDisplay = GDK_WINDOW_XDISPLAY(widget->window); - rootXWindow = RootWindow(dndXDisplay, DefaultScreen(dndXDisplay)); - - /* - * Try and get the Motif drag window property from X's root window. If one - * is provided, a DnD is pending. - */ - ret = XGetWindowProperty(dndXDisplay, rootXWindow, GDKATOM_TO_ATOM(motifDragWindow), - 0, 1, False, XA_WINDOW, - &type, &format, &nitems, &bytesAfter, &prop); - if (ret != Success) { - Warning("DnDGHMotifDragPending: XGetWindowProperty() error.\n"); - return FALSE; - } - - if (type == None) { - Debug("DnDGHXdndDragPending: a Motif drag is not pending\n"); - return FALSE; - } - - Debug("DnDGHXdndDragPending: a Motif drag is pending\n"); - - XFree(prop); - return TRUE; -} - - -/* - *---------------------------------------------------------------------------- - * - * DnDGHFileListClear -- - * - * Clears existing Guest->Host file list, releasing any used resources. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static INLINE void -DnDGHFileListClear(void) -{ - Debug("DnDGHFileListClear: clearing G->H file list\n"); - if (gGHState.dndFileList) { - free(gGHState.dndFileList); - gGHState.dndFileList = NULL; - } - gGHState.dndFileListSize = 0; - gGHState.dndFileListNext = NULL; -} - - -/* - *---------------------------------------------------------------------------- - * - * DnDGHFileListSet -- - * - * Sets the Guest->Host file list that is accessed through - * DnDGHFileListGetNext(). - * - * Results: - * None. - * - * Side effects: - * Clears the existing Guest->Host file list if it exists. - * - *---------------------------------------------------------------------------- - */ - -static INLINE void -DnDGHFileListSet(char *fileList, // IN: new Guest->Host file list - size_t fileListSize) // IN: size of the provided list -{ - DnDGHFileListClear(); - gGHState.dndFileList = fileList; - gGHState.dndFileListSize = fileListSize; - gGHState.dndFileListNext = fileList; - - Debug("DnDGHFileListSet: [%s] (%"FMTSZ"u)\n", - CPName_Print(gGHState.dndFileList, gGHState.dndFileListSize), - gGHState.dndFileListSize); -} - - -/* - *---------------------------------------------------------------------------- - * - * DnDGHFileListGetNext -- - * - * Retrieves the next file in the Guest->Host file list. - * - * Note that this function may only be called after calling - * DnDGHFileListSet() and before calling DnDGHFileListClear(). - * - * Results: - * TRUE on success, FALSE on failure. If TRUE is returned, fileName is - * given a pointer to the filename's location or NULL if there are no more - * files, and fileNameSize is given the length of fileName. - * - * Side effects: - * The fileListNext value of the Guest->Host global state is updated. - * - *---------------------------------------------------------------------------- - */ - -static INLINE Bool -DnDGHFileListGetNext(char **fileName, // OUT: fill with filename location - size_t *fileNameSize) // OUT: fill with filename length -{ - char const *end; - char const *next; - int len; - - ASSERT(gGHState.dndFileList); - ASSERT(gGHState.dndFileListNext); - ASSERT(gGHState.dndFileListSize > 0); - - /* Ensure end is the last NUL character */ - end = CPNameUtil_Strrchr(gGHState.dndFileList, gGHState.dndFileListSize, '\0'); - ASSERT(end); - - /* Get the length of this filename and a pointer to the next one */ - len = CPName_GetComponent(gGHState.dndFileListNext, end, &next); - if (len < 0) { - Warning("DnDGHFileListGetNext: error retrieving next component\n"); - return FALSE; - } - - /* No more entries in the list */ - if (len == 0) { - Debug("DnDGHFileListGetNext: no more entries\n"); - *fileName = NULL; - *fileNameSize = 0; - return TRUE; - } - - Debug("DnDGHFileListGetNext: returning [%s] (%d)\n", - gGHState.dndFileListNext, len); - - *fileName = gGHState.dndFileListNext; - *fileNameSize = len; - gGHState.dndFileListNext = (char *)next; - return TRUE; -} - - -/* - *---------------------------------------------------------------------------- - * - * DnDGHStateInit -- - * - * Initializes the Guest->Host DnD state. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static INLINE void -DnDGHStateInit(GtkWidget *widget) // IN -{ - Debug("DnDGHStateInit: initializing guest->host state\n"); - gGHState.time = 0; - gGHState.dragContext = NULL; - gGHState.dragInProgress = FALSE; - gGHState.ungrabReceived = FALSE; - gGHState.event = NULL; - DnDGHXdndClearPending(widget); - if (!gUnity) { - gtk_widget_hide(widget); - } -} - - -/* - *---------------------------------------------------------------------------- - * - * DnDHGStateInit -- - * - * Initialize the Host->Guest DnD state. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static INLINE void -DnDHGStateInit(void) -{ - gHGDnDInProgress = FALSE; - gDoneDragging = FALSE; -} - - -/* - *---------------------------------------------------------------------------- - * - * DnDGHCancel -- - * - * Resets state and sends a DnD cancel message to the host. - * - * Results: - * TRUE on success, FALSE on failure. - * - * Side effects: - * DnD operation is cancelled. - * - *---------------------------------------------------------------------------- - */ - -static INLINE Bool -DnDGHCancel(GtkWidget *widget) // IN: program's widget -{ - /* Hide our widget so we don't receive stray signals */ - if (widget && !gUnity) { - gtk_widget_hide(widget); - } - - if (gGHState.dragContext) { - gdk_drag_status(gGHState.dragContext, 0, gGHState.time); - } - - gGHState.dragInProgress = FALSE; - - /* - * We don't initialize Guest->Host state here since an ungrab/grab/ungrab - * will cause a cancel but we want the drop of the DnD to still work. - */ - return RpcOut_sendOne(NULL, NULL, "dnd.finish cancel"); -} - - -/* - *---------------------------------------------------------------------------- - * - * DnDGHXEventTimeout -- - * - * Cleans up after fake X events do not cause intended events. Hides the - * provided widget and resets all Guest->Host DnD state. - * - * Note that this is expected to occur on ungrab if there is not a DnD - * pending, but may also occur at other times (sometimes we do not receive - * the drag drop after the mouse button release is faked on KDE). - * - * This function is invoked by the event manager; it is added/removed - * to/from the queue in both DnDRpcInMouseUngrabCB() and DnDRpcInFinishCB(), - * and DnDGtkDragMotionCB() and DnDGtkDragDropCB() respectively. - * - * Results: - * TRUE always, so the event manager doesn't stop running. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static Bool -DnDGHXEventTimeout(void *clientData) // IN: our widget -{ - GtkWidget *widget = (GtkWidget *)clientData; - - Debug("DnDGHXEventTimeout time out \n"); - - RpcOut_sendOne(NULL, NULL, "dnd.notpending"); - - if (!gGHState.dragInProgress && !gUnity) { - gtk_widget_hide(widget); - } - - /* gGHState.event is cleared with the rest of Guest->Host state */ - DnDGHStateInit(widget); - - return TRUE; -} - - -/* - * Public functions invoked by the rest of vmware-user - */ - -/* - *----------------------------------------------------------------------------- - * - * DnD_GetVmxDnDVersion -- - * - * Ask the vmx for it's dnd version. - * - * Results: - * The dnd version the vmx supports, 0 if the vmx doesn't know - * what we're talking about. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -uint32 -DnD_GetVmxDnDVersion(void) -{ - char *reply = NULL; - size_t replyLen; - uint32 vmxVersion; - - if (!RpcOut_sendOne(&reply, &replyLen, "vmx.capability.dnd_version")) { - Debug("DnD_GetVmxDnDVersion: could not get VMX DnD version " - "capability: %s\n", reply ? reply : "NULL"); - vmxVersion = 0; - } else { - vmxVersion = atoi(reply); - ASSERT(vmxVersion > 1); /* DnD versions start at 2 */ - } - - free(reply); - return vmxVersion; -} - - -/* - *----------------------------------------------------------------------------- - * - * DnD_RegisterCapability -- - * - * Register the "dnd" capability. Sometimes this needs to be done separately - * from the rest of DnD registration, so we provide it separately here. - * - * Results: - * TRUE on success - * FALSE on failure - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -DnD_RegisterCapability(void) -{ - /* Tell the VMX about the DnD version we support. */ - if (!RpcOut_sendOne(NULL, NULL, "tools.capability.dnd_version 2")) { - Debug("DnD_RegisterCapability: could not set guest DnD version capability\n"); - return FALSE; - } else if (!DnDSendVmxNewFileRoot("dnd.ready enable")) { - Debug("DnD_RegisterCapability: failed to send dnd.ready message to host\n"); - return FALSE; - } - return TRUE; -} - - -/* - *----------------------------------------------------------------------------- - * - * DnD_Register -- - * - * Register the DnD capability, setup callbacks, initialize. - * - * Results: - * TRUE on success, FALSE otherwise. - * - * Side effects: - * mainWnd will be a dragSource in the guest, and dnd will work from - * host to guest. - * - *----------------------------------------------------------------------------- - */ - -Bool -DnD_Register(GtkWidget *hgWnd, // IN: The widget to register as a drag source. - GtkWidget *ghWnd) // IN: The widget to register as a drag target. -{ - uint32 i; - - gDragCtx = NULL; - - ASSERT(hgWnd); - ASSERT(ghWnd); - - if (DnD_GetVmxDnDVersion() < 2) { - goto error; - } - - /* - * We can't pass in NULL to XTestQueryExtension(), so pass in a dummy - * variable to avoid segfaults. If we have a reason to check the major and - * minor numbers of the running extension, that would go here. - */ - if (!XTestQueryExtension(GDK_WINDOW_XDISPLAY(hgWnd->window), - &i, &i, &i, &i)) { - goto error; - } - - /* Host->Guest RPC callbacks */ - RpcIn_RegisterCallback(gRpcIn, "dnd.data.set", DnDRpcInDataSetCB, hgWnd); - RpcIn_RegisterCallback(gRpcIn, "dnd.enter", DnDRpcInEnterCB, hgWnd); - RpcIn_RegisterCallback(gRpcIn, "dnd.move", DnDRpcInMoveCB, hgWnd); - RpcIn_RegisterCallback(gRpcIn, "dnd.drop", DnDRpcInDropCB, hgWnd); - RpcIn_RegisterCallback(gRpcIn, "dnd.data.finish", DnDRpcInDataFinishCB, - hgWnd); - - /* Guest->Host RPC callbacks */ - RpcIn_RegisterCallback(gRpcIn, "dnd.ungrab", - DnDRpcInMouseUngrabCB, ghWnd); - RpcIn_RegisterCallback(gRpcIn, "dnd.data.get.file", - DnDRpcInGetNextFileCB, ghWnd); - RpcIn_RegisterCallback(gRpcIn, "dnd.finish", - DnDRpcInFinishCB, ghWnd); - - /* - * Setup mainWnd as a DND source/dest. - * - * Note that G->H drag targets should come first in this array. Currently - * G->H only supports text/uri-list targets. - */ - gTargetEntry[0].target = DRAG_TARGET_NAME_URI_LIST; - gTargetEntry[0].info = DRAG_TARGET_INFO_URI_LIST; - gTargetEntry[0].flags = 0; - gTargetEntry[1].target = DRAG_TARGET_NAME_TEXT_PLAIN; - gTargetEntry[1].info = DRAG_TARGET_INFO_TEXT_PLAIN; - gTargetEntry[1].flags = 0; - gTargetEntry[2].target = DRAG_TARGET_NAME_STRING; - gTargetEntry[2].info = DRAG_TARGET_INFO_STRING; - gTargetEntry[2].flags = 0; - - /* Populate our GdkAtom table for our supported Guest->Host targets */ - for (i = 0; - i < ARRAYSIZE(gTargetEntry) && i < ARRAYSIZE(gTargetEntryAtom); - i++) { - gTargetEntryAtom[i] = gdk_atom_intern(gTargetEntry[i].target, FALSE); - } - - /* Drag source for Host->Guest */ - gtk_drag_source_set(hgWnd, GDK_BUTTON1_MASK, - gTargetEntry, ARRAYSIZE(gTargetEntry), - GDK_ACTION_COPY | GDK_ACTION_MOVE); - - gtk_signal_connect(GTK_OBJECT(hgWnd), "drag_begin", - GTK_SIGNAL_FUNC(DnDGtkBeginCB), hgWnd); - gtk_signal_connect(GTK_OBJECT(hgWnd), "drag_end", - GTK_SIGNAL_FUNC(DnDGtkEndCB), hgWnd); - gtk_signal_connect(GTK_OBJECT(hgWnd), "drag_data_get", - GTK_SIGNAL_FUNC(DnDGtkDataRequestCB), hgWnd); - - - /* - * Drop target (destination) for Guest->Host - * - * We provide NR_GH_DRAG_TARGETS (rather than ARRAYSIZE(gTargetEntry)) to - * gtk_drag_dest_set() since we support less targets for G->H than H->G. - */ - gtk_drag_dest_set(ghWnd, - GTK_DEST_DEFAULT_MOTION, - gTargetEntry, NR_GH_DRAG_TARGETS, - GDK_ACTION_COPY | GDK_ACTION_MOVE); - - gtk_signal_connect(GTK_OBJECT(ghWnd), "drag_motion", - GTK_SIGNAL_FUNC(DnDGtkDragMotionCB), ghWnd); - gtk_signal_connect(GTK_OBJECT(ghWnd), "drag_data_received", - GTK_SIGNAL_FUNC(DnDGtkDragDataReceivedCB), - ghWnd); - gtk_signal_connect(GTK_OBJECT(ghWnd), "drag_drop", - GTK_SIGNAL_FUNC(DnDGtkDragDropCB), ghWnd); - - DnD_OnReset(hgWnd, ghWnd); - - if (DnD_RegisterCapability()) { - return TRUE; - } - - /* - * We get here if DnD registration fails for some reason - */ -error: - DnD_Unregister(hgWnd, ghWnd); - return FALSE; -} - - -/* - *----------------------------------------------------------------------------- - * - * DnD_Unregister -- - * - * Cleanup dnd related things. - * - * Results: - * None. - * - * Side effects: - * DnD is stopped, the rpc channel to the vmx is closed. - * - *----------------------------------------------------------------------------- - */ - -void -DnD_Unregister(GtkWidget *hgWnd, // IN: The widget for hg dnd - GtkWidget *ghWnd) // IN: The widget for gh dnd -{ - RpcOut_sendOne(NULL, NULL, "dnd.ready disable"); - - DnDGHFileListClear(); - - /* Unregister source for Host->Guest DnD. */ - gtk_drag_source_unset(hgWnd); - gtk_signal_disconnect_by_func(GTK_OBJECT(hgWnd), - GTK_SIGNAL_FUNC(DnDGtkBeginCB), - hgWnd); - gtk_signal_disconnect_by_func(GTK_OBJECT(hgWnd), - GTK_SIGNAL_FUNC(DnDGtkEndCB), - hgWnd); - gtk_signal_disconnect_by_func(GTK_OBJECT(hgWnd), - GTK_SIGNAL_FUNC(DnDGtkDataRequestCB), - hgWnd); - - /* Unregister destination for Guest->Host DnD. */ - gtk_drag_dest_unset(ghWnd); - gtk_signal_disconnect_by_func(GTK_OBJECT(ghWnd), - GTK_SIGNAL_FUNC(DnDGtkDragMotionCB), - ghWnd); - gtk_signal_disconnect_by_func(GTK_OBJECT(ghWnd), - GTK_SIGNAL_FUNC(DnDGtkDragDataReceivedCB), - ghWnd); - gtk_signal_disconnect_by_func(GTK_OBJECT(ghWnd), - GTK_SIGNAL_FUNC(DnDGtkDragDropCB), - ghWnd); - - RpcIn_UnregisterCallback(gRpcIn, "dnd.data.set"); - RpcIn_UnregisterCallback(gRpcIn, "dnd.enter"); - RpcIn_UnregisterCallback(gRpcIn, "dnd.move"); - RpcIn_UnregisterCallback(gRpcIn, "dnd.drop"); - RpcIn_UnregisterCallback(gRpcIn, "dnd.data.finish"); - - /* Guest->Host RPC callbacks */ - RpcIn_UnregisterCallback(gRpcIn, "dnd.ungrab"); - RpcIn_UnregisterCallback(gRpcIn, "dnd.data.get.file"); - RpcIn_UnregisterCallback(gRpcIn, "dnd.finish"); -} - - -/* - *---------------------------------------------------------------------------- - * - * DnD_OnReset -- - * - * Handles reinitializing DnD state on a reset. - * - * Results: - * None. - * - * Side effects: - * DnD is stopped and restarted. - * - *---------------------------------------------------------------------------- - */ - -void -DnD_OnReset(GtkWidget *hgWnd, // IN: The widget for hg dnd - GtkWidget *ghWnd) // IN: The widget for gh dnd - -{ - Debug("DnD_OnReset: entry\n"); - - /* Cancel file transfer. */ - if (gHGDnDInProgress || gHGDataPending) { - DnD_DeleteStagingFiles(gFileRoot, FALSE); - if (DnD_BlockIsReady(&gBlockCtrl) && - !gBlockCtrl.RemoveBlock(gBlockCtrl.fd, gFileRoot)) { - Warning("DnD_OnReset: could not remove block on %s\n", - gFileRoot); - } - } - - /* - * If a DnD in either direction was in progress during suspend, send an - * escape to cancel the operation and reset the pointer state. - */ - if (gHGDnDInProgress) { - Debug("DnD_OnReset: sending hgWnd escape\n"); - DnDSendEscapeKey(hgWnd); - } - - if (gGHState.dragInProgress) { - Debug("DnD_OnReset: sending ghWnd escape\n"); - DnDSendEscapeKey(ghWnd); - } - - if (gGHState.dragInProgress) { - Debug("DnD_OnReset: canceling host->guest DnD\n"); - DnDGHCancel(ghWnd); - } - - /* Reset DnD state. */ - DnDHGStateInit(); - DnDGHStateInit(ghWnd); - DnDGHFileListClear(); - DnD_SetMode(FALSE); -} - - -/* - *---------------------------------------------------------------------------- - * - * DnD_InProgress -- - * - * Indicates whether a DnD (or its data transfer) is currently in progress. - * - * Results: - * TRUE if a DnD is in progress, FALSE otherwise. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -Bool -DnD_InProgress(void) -{ - return gGHState.dragInProgress || gHGDnDInProgress || gHGDataPending; -} - - -/* - *---------------------------------------------------------------------------- - * - * DnD_SetMode -- - * - * Sets dnd mode to single window or unity mode. - * - * Results: - * None. - * - * Side effects: - * Controls if the g->h det window is automatically hidden. - * - *---------------------------------------------------------------------------- - */ - -void -DnD_SetMode(Bool unity) // IN -{ - gUnity = unity; -} diff --git a/open-vm-tools/vmware-user/dndUI.cpp b/open-vm-tools/vmware-user/dndUI.cpp deleted file mode 100644 index 1b96ef09d..000000000 --- a/open-vm-tools/vmware-user/dndUI.cpp +++ /dev/null @@ -1,2019 +0,0 @@ -/********************************************************* - * Copyright (C) 2009 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - -/** - * @file dndUI.cpp -- - * - * This class implements stubs for the methods that allow DnD between - * host and guest. - */ - -#include "dndUI.h" - -extern "C" { -#include "vmwareuserInt.h" -#include "vmblock.h" -#include "file.h" -#include "dnd.h" -#include "dndMsg.h" -#include "dndClipboard.h" -#include "cpName.h" -#include "debug.h" -#include "cpNameUtil.h" -#include "hostinfo.h" -#include "rpcout.h" -#include "eventManager.h" -#include "unity.h" -#include -#include /* for XTest*() */ -#include "vmware/guestrpc/tclodefs.h" -} - -#include "dndGuest.h" -#include "copyPasteDnDWrapper.h" - -/** - * - * Constructor. - */ - -DnDUI::DnDUI(DblLnkLst_Links *eventQueue) - : m_eventQueue(eventQueue), - m_DnD(NULL), - m_detWnd(NULL), - m_blockCtrl(NULL), - m_HGGetDataInProgress(false), - m_blockAdded(false), - m_GHDnDInProgress(false), - m_GHDnDDataReceived(false), - m_unityMode(false), - m_inHGDrag(false), - m_effect(DROP_NONE), - m_isFileDnD(false), - m_mousePosX(0), - m_mousePosY(0), - m_dc(NULL), - m_destDropTime(0) -{ - Debug("%s: enter\n", __FUNCTION__); -} - - -/** - * - * Destructor. - */ - -DnDUI::~DnDUI() -{ - Debug("%s: enter\n", __FUNCTION__); - if (m_DnD) { - delete m_DnD; - } - if (m_detWnd) { - delete m_detWnd; - } - CPClipboard_Destroy(&m_clipboard); -} - - -/** - * - * Initialize DnDUI object. - */ - -bool -DnDUI::Init() -{ - Debug("%s: enter\n", __FUNCTION__); - char *reply = NULL; - size_t replyLen; - bool ret = true; - - ASSERT(m_eventQueue); - CPClipboard_Init(&m_clipboard); - m_DnD = new DnD(m_eventQueue); - if (!m_DnD) { - Debug("%s: unable to allocate DnD object\n", __FUNCTION__); - goto fail; - } - m_detWnd = new DragDetWnd(); - if (!m_detWnd) { - Debug("%s: unable to allocate DragDetWnd object\n", __FUNCTION__); - goto fail; - } - -#if defined(DETWNDDEBUG) - - /* - * This code can only be called when DragDetWnd is derived from - * Gtk::Window. The normal case is that DragDetWnd is an instance of - * Gtk::Invisible, which doesn't implement the methods that SetAttributes - * relies upon. - */ - - m_detWnd->SetAttributes(); -#endif - - SetTargetsAndCallbacks(); - - /* Exchange dnd version information with the VMX */ - if (!RpcOut_sendOne(NULL, NULL, "tools.capability.dnd_version 3")) { - Debug("%s: could not set guest dnd version capability\n", __FUNCTION__); - goto fail; - } else { - if (!RpcOut_sendOne(&reply, &replyLen, "vmx.capability.dnd_version")) { - Debug("%s: could not get VMX dnd version capability\n", - __FUNCTION__); - goto fail; - } else if (atoi(reply) < 3) { - Debug("%s: VMX DnD version is less than 3.\n", __FUNCTION__); - goto fail; - } - } - - Debug("%s: VMX version ok: %d\n", __FUNCTION__, atoi(reply)); - - /* Set common layer callbacks. */ - m_DnD->dragStartChanged.connect( - sigc::mem_fun(this, &DnDUI::CommonDragStartCB)); - m_DnD->fileCopyDoneChanged.connect( - sigc::mem_fun(this, &DnDUI::CommonSourceFileCopyDoneCB)); - m_DnD->updateDetWndChanged.connect( - sigc::mem_fun(this, &DnDUI::CommonUpdateDetWndCB)); - m_DnD->updateUnityDetWndChanged.connect( - sigc::mem_fun(this, &DnDUI::CommonUpdateUnityDetWndCB)); - m_DnD->moveDetWndToMousePos.connect( - sigc::mem_fun(this, &DnDUI::CommonMoveDetWndToMousePos)); - m_DnD->sourceCancelChanged.connect( - sigc::mem_fun(this, &DnDUI::CommonSourceCancelCB)); - m_DnD->targetPrivateDropChanged.connect( - sigc::mem_fun(this, &DnDUI::CommonDestPrivateDropCB)); - m_DnD->ghCancel.connect( - sigc::mem_fun(this, &DnDUI::CommonDestCancelCB)); - m_DnD->sourceDropChanged.connect( - sigc::mem_fun(this, &DnDUI::CommonSourceDropCB)); - m_DnD->updateMouseChanged.connect( - sigc::mem_fun(this, &DnDUI::CommonUpdateMouseCB)); - - /* Set Gtk+ callbacks for source. */ - m_detWnd->signal_drag_begin().connect( - sigc::mem_fun(this, &DnDUI::GtkSourceDragBeginCB)); - m_detWnd->signal_drag_data_get().connect( - sigc::mem_fun(this, &DnDUI::GtkSourceDragDataGetCB)); - m_detWnd->signal_drag_end().connect( - sigc::mem_fun(this, &DnDUI::GtkSourceDragEndCB)); - - CommonUpdateDetWndCB(false, 0, 0); - CommonUpdateUnityDetWndCB(false, 0, false); - goto out; -fail: - ret = false; - if (m_DnD) { - delete m_DnD; - m_DnD = NULL; - } - if (m_detWnd) { - delete m_detWnd; - m_detWnd = NULL; - } -out: - if (reply) { - free(reply); - } - return ret; -} - - -/** - * - * Setup targets we support, claim ourselves as a drag destination, and - * register callbacks for Gtk+ drag and drop callbacks the platform will - * send to us. - */ - -void -DnDUI::SetTargetsAndCallbacks() -{ - Debug("%s: enter\n", __FUNCTION__); - - /* Construct supported target list for HG DnD. */ - std::list targets; - - /* File DnD. */ - targets.push_back(Gtk::TargetEntry(DRAG_TARGET_NAME_URI_LIST)); - - /* RTF text DnD. */ - targets.push_back(Gtk::TargetEntry(TARGET_NAME_APPLICATION_RTF)); - targets.push_back(Gtk::TargetEntry(TARGET_NAME_TEXT_RICHTEXT)); - - /* Plain text DnD. */ - targets.push_back(Gtk::TargetEntry(TARGET_NAME_STRING)); - targets.push_back(Gtk::TargetEntry(TARGET_NAME_TEXT_PLAIN)); - targets.push_back(Gtk::TargetEntry(TARGET_NAME_UTF8_STRING)); - targets.push_back(Gtk::TargetEntry(TARGET_NAME_COMPOUND_TEXT)); - - /* - * We don't want Gtk handling any signals for us, we want to - * do it ourselves based on the results from the guest. - * - * Second argument in drag_dest_set defines the automatic behaviour options - * of the destination widget. We used to not define it (0) and in some - * distributions (like Ubuntu 6.10) DragMotion only get called once, - * and not send updated mouse position to guest, and also got cancel - * signal when user drop the file (bug 175754). With flag DEST_DEFAULT_MOTION - * the bug is fixed. Almost all other example codes use DEST_DEFAULT_ALL - * but in our case, we will call drag_get_data during DragMotion, and - * will cause X dead with DEST_DEFAULT_ALL. The reason is unclear. - */ - m_detWnd->drag_dest_set(targets, Gtk::DEST_DEFAULT_MOTION, - Gdk::ACTION_COPY | Gdk::ACTION_MOVE); - m_detWnd->signal_drag_leave().connect(sigc::mem_fun(this, &DnDUI::GtkDestDragLeaveCB)); - m_detWnd->signal_drag_motion().connect(sigc::mem_fun(this, &DnDUI::GtkDestDragMotionCB)); - m_detWnd->signal_drag_drop().connect(sigc::mem_fun(this, &DnDUI::GtkDestDragDropCB)); - m_detWnd->signal_drag_data_received().connect(sigc::mem_fun(this, &DnDUI::GtkDestDragDataReceivedCB)); -} - -/* Begin of callbacks issued by common layer code */ - -/** - * - * Reset Callback to reset dnd ui state. - */ - -void -DnDUI::CommonResetCB(void) -{ - Debug("%s: entering\n", __FUNCTION__); - m_GHDnDDataReceived = false; - m_HGGetDataInProgress = false; - m_GHDnDInProgress = false; - m_effect = DROP_NONE; - m_inHGDrag = false; - m_dc = NULL; - m_isFileDnD = false; - RemoveBlock(); -} - - -/** - * - * Cancel any DnD file transfers and reset. - */ - -void -DnDUI::Cancel() -{ - Debug("%s: enter\n", __FUNCTION__); - if (m_blockAdded) { - /* - * If we don't do this, the destination will have something to - * copy, and likely truncated. So remove it. - */ - DnD_DeleteStagingFiles(m_HGStagingDir.c_str(), false); - } - CommonResetCB(); -} - - -/* Source functions for HG DnD. */ - -/** - * - * Called when host successfully detected a pending HG drag. - * - * param[in] clip cross-platform clipboard - * param[in] stagingDir associated staging directory - */ - -void -DnDUI::CommonDragStartCB(const CPClipboard *clip, std::string stagingDir) -{ - Glib::RefPtr targets; - Gdk::DragAction actions; - GdkEventMotion event; - - CPClipboard_Clear(&m_clipboard); - CPClipboard_Copy(&m_clipboard, clip); - - Debug("%s: enter\n", __FUNCTION__); - - /* - * Before the DnD, we should make sure that the mouse is released - * otherwise it may be another DnD, not ours. Send a release, then - * a press here to cover this case. - */ - SendFakeXEvents(false, true, false, false, false, 0, 0); - SendFakeXEvents(true, true, true, false, true, 0, 0); - - /* - * Construct the target and action list, as well as a fake motion notify - * event that's consistent with one that would typically start a drag. - */ - targets = Gtk::TargetList::create(std::list()); - - if (CPClipboard_ItemExists(&m_clipboard, CPFORMAT_FILELIST)) { - m_HGStagingDir = stagingDir; - if (!m_HGStagingDir.empty()) { - targets->add(Glib::ustring(DRAG_TARGET_NAME_URI_LIST)); - /* Add private data to tag dnd as originating from this vm. */ - char *pid; - Debug("%s: adding re-entrant drop target, pid %d\n", __FUNCTION__, getpid()); - pid = Str_Asprintf(NULL, "guest-dnd-target %d", static_cast(getpid())); - if (pid) { - targets->add(Glib::ustring(pid)); - free(pid); - } - } - } - - if (CPClipboard_ItemExists(&m_clipboard, CPFORMAT_FILECONTENTS)) { - if (WriteFileContentsToStagingDir()) { - targets->add(Glib::ustring(DRAG_TARGET_NAME_URI_LIST)); - } - } - - if (CPClipboard_ItemExists(&m_clipboard, CPFORMAT_TEXT)) { - targets->add(Glib::ustring(TARGET_NAME_STRING)); - targets->add(Glib::ustring(TARGET_NAME_TEXT_PLAIN)); - targets->add(Glib::ustring(TARGET_NAME_UTF8_STRING)); - targets->add(Glib::ustring(TARGET_NAME_COMPOUND_TEXT)); - } - - if (CPClipboard_ItemExists(&m_clipboard, CPFORMAT_RTF)) { - targets->add(Glib::ustring(TARGET_NAME_APPLICATION_RTF)); - targets->add(Glib::ustring(TARGET_NAME_TEXT_RICHTEXT)); - } - - actions = Gdk::ACTION_COPY | Gdk::ACTION_MOVE; - - /* TODO set the x/y coords to the actual drag initialization point. */ - event.type = GDK_MOTION_NOTIFY; - event.window = m_detWnd->get_window()->gobj(); - event.send_event = false; - event.time = GDK_CURRENT_TIME; - event.x = 10; - event.y = 10; - event.axes = NULL; - event.state = GDK_BUTTON1_MASK; - event.is_hint = 0; - event.device = gdk_device_get_core_pointer(); - event.x_root = 0; - event.y_root = 5; - - /* Tell Gtk that a drag should be started from this widget. */ - m_detWnd->drag_begin(targets, actions, 1, (GdkEvent *)&event); - m_blockAdded = false; - m_isFileDnD = false; - SourceDragStartDone(); - /* Initialize host hide feedback to DROP_NONE. */ - m_effect = DROP_NONE; - SourceUpdateFeedback(m_effect); -} - - -/** - * - * Cancel current HG DnD. - */ - -void -DnDUI::CommonSourceCancelCB(void) -{ - Debug("%s: entering\n", __FUNCTION__); - - /* - * Force the window to show, position the mouse over it, and release. - * Seems like moving the window to 0, 0 eliminates frequently observed - * flybacks when we cancel as user moves mouse in and out of destination - * window in a H->G DnD. - */ - CommonUpdateDetWndCB(true, 0, 0); - SendFakeXEvents(true, true, false, true, true, 0, 0); - CommonUpdateDetWndCB(false, 0, 0); - m_inHGDrag = false; - m_HGGetDataInProgress = false; - m_effect = DROP_NONE; - RemoveBlock(); -} - - -/** - * - * Handle common layer private drop CB. - * - * @param[in] x position to release the mouse button (ignored). - * @param[in] y position to release the mouse button (ignored). - * - * @note We ignore the coordinates, because we just need to release the mouse - * in its current position. - */ - -void -DnDUI::CommonDestPrivateDropCB(int32 x, - int32 y) -{ - Debug("%s: entering\n", __FUNCTION__); - /* Unity manager in host side may already send the drop into guest. */ - if (m_GHDnDInProgress) { - - /* - * Release the mouse button. - */ - SendFakeXEvents(false, true, false, false, false, 0, 0); - } - CommonResetCB(); -} - - -/** - * - * Cancel current DnD (G->H only). - */ - -void -DnDUI::CommonDestCancelCB(void) -{ - Debug("%s: entering\n", __FUNCTION__); - /* Unity manager in host side may already send the drop into guest. */ - if (m_GHDnDInProgress) { - CommonUpdateDetWndCB(true, 0, 0); - - /* - * Show the window, move it to the mouse position, and release the - * mouse button. - */ - SendFakeXEvents(true, true, false, true, false, 0, 0); - } - m_destDropTime = GetTimeInMillis(); - CommonResetCB(); -} - - -/** - * - * Got drop from host side. Release the mouse button in the detection window - */ - -void -DnDUI::CommonSourceDropCB(void) -{ - Debug("%s: enter\n", __FUNCTION__); - CommonUpdateDetWndCB(true, 0, 0); - - /* - * Move the mouse to the saved coordinates, and release the mouse button. - */ - SendFakeXEvents(false, true, false, false, true, m_mousePosX, m_mousePosY); - CommonUpdateDetWndCB(false, 0, 0); -} - - -/** - * - * Callback when file transfer is done, which finishes the file - * copying from host to guest staging directory. - * - * @param[in] success if true, transfer was successful - * @param[in] path of staging dir (which will have a block that needs removing) - */ - -void -DnDUI::CommonSourceFileCopyDoneCB(bool success, - std::vector stagingDir) -{ - Debug("%s: %s\n", __FUNCTION__, success ? "success" : "failed"); - /* Copied files are already removed in common layer. */ - stagingDir.clear(); - CommonResetCB(); - m_HGGetDataInProgress = false; -} - - -/** - * - * Shows/hides drag detection windows based on the mask. - * - * @param[in] bShow if true, show the window, else hide it. - * @param[in] x x-coordinate to which the detection window needs to be moved - * @param[in] y y-coordinate to which the detection window needs to be moved - */ - -void -DnDUI::CommonUpdateDetWndCB(bool bShow, - int32 x, - int32 y) -{ - Debug("%s: enter 0x%lx show %d x %d y %d\n", - __FUNCTION__, - (unsigned long) m_detWnd->get_window()->gobj(), bShow, x, y); - - /* If the window is being shown, move it to the right place. */ - if (bShow) { - x = MAX(x - DRAG_DET_WINDOW_WIDTH / 2, 0); - y = MAX(y - DRAG_DET_WINDOW_WIDTH / 2, 0); - - m_detWnd->Show(); - m_detWnd->Raise(); - m_detWnd->SetGeometry(x, y, DRAG_DET_WINDOW_WIDTH * 2, DRAG_DET_WINDOW_WIDTH * 2); - Debug("%s: show at (%d, %d, %d, %d)\n", __FUNCTION__, x, y, DRAG_DET_WINDOW_WIDTH * 2, DRAG_DET_WINDOW_WIDTH * 2); - /* - * Wiggle the mouse here. Especially for G->H DnD, this improves - * reliability of making the drag escape the guest window immensly. - * Stolen from the legacy V2 DnD code. - */ - - SendFakeMouseMove(x, y); - m_detWnd->SetIsVisible(true); - } else { - Debug("%s: hide\n", __FUNCTION__); - m_detWnd->Hide(); - m_detWnd->SetIsVisible(false); - } -} - - -/** - * - * Shows/hides full-screen Unity drag detection window. - * - * @param[in] bShow if true, show the window, else hide it. - * @param[in] unityWndId active front window - * @param[in] bottom if true, adjust the z-order to be bottom most. - */ - -void -DnDUI::CommonUpdateUnityDetWndCB(bool bShow, - uint32 unityWndId, - bool bottom) -{ - Debug("%s: enter 0x%lx unityID 0x%x\n", - __FUNCTION__, - (unsigned long) m_detWnd->get_window()->gobj(), - unityWndId); - if (bShow && ((unityWndId > 0) || bottom)) { - int width = m_detWnd->GetScreenWidth(); - int height = m_detWnd->GetScreenHeight(); - m_detWnd->SetGeometry(0, 0, width, height); - m_detWnd->Show(); - if (bottom) { - m_detWnd->Lower(); - } - - Debug("%s: show, (0, 0, %d, %d)\n", __FUNCTION__, width, height); - } else { - if (m_detWnd->GetIsVisible() == true) { - if (m_unityMode) { - - /* - * Show and move detection window to current mouse position - * and resize. - */ - SendFakeXEvents(true, false, true, true, false, 0, 0); - } - } else { - m_detWnd->Hide(); - Debug("%s: hide\n", __FUNCTION__); - } - } -} - - -/** - * - * Move detection windows to current cursor position. - */ - -void -DnDUI::CommonMoveDetWndToMousePos(void) -{ - SendFakeXEvents(true, false, true, true, false, 0, 0); -} - - -/** - * - * Handle request from common layer to update mouse position. - * - * @param[in] x x coordinate of pointer - * @param[in] y y coordinate of pointer - */ - -void -DnDUI::CommonUpdateMouseCB(int32 x, - int32 y) -{ - // Position the pointer, and record its position. - - SendFakeXEvents(false, false, false, false, true, x, y); - m_mousePosX = x; - m_mousePosY = y; - - if (m_dc && !m_GHDnDInProgress) { - - // If we are the context of a DnD, send DnD feedback to the source. - - DND_DROPEFFECT effect; - effect = ToDropEffect((Gdk::DragAction)(m_dc->action)); - if (effect != m_effect) { - m_effect = effect; - Debug("%s: Updating feedback\n", __FUNCTION__); - SourceUpdateFeedback(m_effect); - } - } -} - -/* Beginning of Gtk+ Callbacks */ - -/* - * Source callbacks from Gtk+. Most are seen only when we are acting as a - * drag source. - */ - -/** - * - * "drag_motion" signal handler for GTK. We should respond by setting drag - * status. Note that there is no drag enter signal. We need to figure out - * if a new drag is happening on our own. Also, we don't respond with a - * "allowed" drag status right away, we start a new drag operation over VMDB - * (which tries to notify the host of the new operation). Once the host has - * responded), we respond with a proper drag status. - * - * @param[in] dc associated drag context - * @param[in] x x coordinate of the drag motion - * @param[in] y y coordinate of the drag motion - * @param[in] time time of the drag motion - * - * @return returning false means we won't get notified of future motion. So, - * we only return false if we don't recognize the types being offered. We - * return true otherwise, even if we don't accept the drag right now for some - * other reason. - * - * @note you may see this callback during DnD when detection window is acting - * as a source. In that case it will be ignored. In a future refactoring, - * we will try and avoid this. - */ - -bool -DnDUI::GtkDestDragMotionCB(const Glib::RefPtr &dc, - int x, - int y, - guint timeValue) -{ - /* - * If this is a Host to Guest drag, we are done here, so return. - */ - unsigned long curTime = GetTimeInMillis(); - Debug("%s: enter dc %p, m_dc %p\n", __FUNCTION__, - dc ? dc->gobj() : NULL, m_dc ? m_dc : NULL); - if (curTime - m_destDropTime <= 1000) { - Debug("%s: ignored %ld %ld %ld\n", __FUNCTION__, - curTime, m_destDropTime, curTime - m_destDropTime); - return true; - } - - Debug("%s: not ignored %ld %ld %ld\n", __FUNCTION__, - curTime, m_destDropTime, curTime - m_destDropTime); - - if (m_inHGDrag || m_HGGetDataInProgress) { - Debug("%s: ignored not in hg drag or not getting hg data\n", __FUNCTION__); - return true; - } - - Gdk::DragAction srcActions; - Gdk::DragAction suggestedAction; - Gdk::DragAction dndAction = (Gdk::DragAction)0; - Glib::ustring target = m_detWnd->drag_dest_find_target(dc); - - if (!m_DnD->IsDnDAllowed()) { - Debug("%s: No dnd allowed!\n", __FUNCTION__); - dc->drag_status(dndAction, timeValue); - return true; - } - - /* Check if dnd began from this vm. */ - - /* - * TODO: Once we upgrade to shipping gtkmm 2.12, we can go back to - * Gdk::DragContext::get_targets, but API/ABI broke between 2.10 and - * 2.12, so we work around it like this for now. - */ - Glib::ListHandle targets( - dc->gobj()->targets, Glib::OWNERSHIP_NONE); - - std::list as = targets; - std::list::iterator result; - char *pid; - pid = Str_Asprintf(NULL, "guest-dnd-target %d", static_cast(getpid())); - if (pid) { - result = std::find(as.begin(), as.end(), std::string(pid)); - free(pid); - } else { - result = as.end(); - } - if (result != as.end()) { - Debug("%s: found re-entrant drop target, pid %s\n", __FUNCTION__, pid ); - return true; - } - - m_dc = dc->gobj(); - - if (target != "") { - /* - * We give preference to the suggested action from the source, and prefer - * copy over move. - */ - suggestedAction = dc->get_suggested_action(); - srcActions = dc->get_actions(); - if (suggestedAction == Gdk::ACTION_COPY || suggestedAction == Gdk::ACTION_MOVE) { - dndAction = suggestedAction; - } else if (srcActions & Gdk::ACTION_COPY) { - dndAction= Gdk::ACTION_COPY; - } else if (srcActions & Gdk::ACTION_MOVE) { - dndAction = Gdk::ACTION_MOVE; - } else { - dndAction = (Gdk::DragAction)0; - } - } else { - dndAction = (Gdk::DragAction)0; - } - - if (dndAction != (Gdk::DragAction)0) { - dc->drag_status(dndAction, timeValue); - if (!m_GHDnDInProgress) { - Debug("%s: new drag, need to get data for host\n", __FUNCTION__); - /* - * This is a new drag operation. We need to start a drag thru the - * backdoor, and to the host. Before we can tell the host, we have to - * retrieve the drop data. - */ - m_GHDnDInProgress = true; - /* only begin drag enter after we get the data */ - /* Need to grab all of the data. */ - m_detWnd->drag_get_data(dc, target, timeValue); - } else { - Debug("%s: Multiple drag motions before gh data has been received.\n", - __FUNCTION__); - } - } else { - Debug("%s: Invalid drag\n", __FUNCTION__); - return false; - } - return true; -} - - -/** - * - * "drag_leave" signal handler for GTK. Log the reception of this signal, - * but otherwise unhandled in our implementation. - * - * @param[in] dc drag context - * @param[in] time time of the drag - */ - -void -DnDUI::GtkDestDragLeaveCB(const Glib::RefPtr &dc, - guint time) -{ - Debug("%s: enter dc %p, m_dc %p\n", __FUNCTION__, - dc ? dc->gobj() : NULL, m_dc ? m_dc : NULL); - - /* - * If we reach here after reset DnD, or we are getting a late - * DnD drag leave signal (we have started another DnD), then - * finish the old DnD. Otherwise, Gtk will not reset and a new - * DnD will not start until Gtk+ times out (which appears to - * be 5 minutes). - * See http://bugzilla.eng.vmware.com/show_bug.cgi?id=528320 - */ - if (!m_dc || dc->gobj() != m_dc) { - Debug("%s: calling drag_finish\n", __FUNCTION__); - dc->drag_finish(true, false, time); - } -} - - -/* - * Gtk+ callbacks that are seen when we are a drag source. - */ - -/** - * - * "drag_begin" signal handler for GTK. - * - * @param[in] context drag context - */ - -void -DnDUI::GtkSourceDragBeginCB(const Glib::RefPtr& context) -{ - Debug("%s: enter dc %p, m_dc %p\n", __FUNCTION__, - context ? context->gobj() : NULL, m_dc ? m_dc : NULL); - m_dc = context->gobj(); -} - - -/** - * - * "drag_data_get" handler for GTK. We don't send drop until we are done. - * - * @param[in] dc drag state - * @param[in] selection_data buffer for data - * @param[in] info unused - * @param[in] time timestamp - * - * @note if the drop has occurred, the files are copied from the guest. - * - *----------------------------------------------------------------------------- - */ - -void -DnDUI::GtkSourceDragDataGetCB(const Glib::RefPtr &dc, - Gtk::SelectionData& selection_data, - guint info, - guint time) -{ - size_t index = 0; - std::string str; - std::string uriList; - std::string stagingDirName; - void *buf; - size_t sz; - utf::utf8string hgData; - DnDFileList fList; - std::string pre; - std::string post; - - const utf::string target = selection_data.get_target().c_str(); - - selection_data.set(target.c_str(), ""); - - Debug("%s: enter dc %p, m_dc %p with target %s\n", __FUNCTION__, - dc ? dc->gobj() : NULL, m_dc ? m_dc : NULL, - target.c_str()); - - if (!m_inHGDrag) { - Debug("%s: not in drag, return\n", __FUNCTION__); - return; - } - - if (target == DRAG_TARGET_NAME_URI_LIST && - CPClipboard_GetItem(&m_clipboard, CPFORMAT_FILELIST, &buf, &sz)) { - - /* Provide path within vmblock file system instead of actual path. */ - stagingDirName = GetLastDirName(m_HGStagingDir); - if (stagingDirName.length() == 0) { - Debug("%s: Cannot get staging directory name, stagingDir: %s\n", - __FUNCTION__, m_HGStagingDir.c_str()); - return; - } - - if (!fList.FromCPClipboard(buf, sz)) { - Debug("%s: Can't get data from clipboard\n", __FUNCTION__); - return; - } - - /* Provide URIs for each path in the guest's file list. */ - if (FCP_TARGET_INFO_GNOME_COPIED_FILES == info) { - pre = FCP_GNOME_LIST_PRE; - post = FCP_GNOME_LIST_POST; - } else if (FCP_TARGET_INFO_URI_LIST == info) { - pre = DND_URI_LIST_PRE_KDE; - post = DND_URI_LIST_POST; - } else { - Debug("%s: Unknown request target: %s\n", __FUNCTION__, - selection_data.get_target().c_str()); - return; - } - - /* Provide path within vmblock file system instead of actual path. */ - hgData = fList.GetRelPathsStr(); - - /* Provide URIs for each path in the guest's file list. */ - while ((str = GetNextPath(hgData, index).c_str()).length() != 0) { - uriList += pre; - if (DnD_BlockIsReady(m_blockCtrl)) { - uriList += m_blockCtrl->blockRoot; - uriList += DIRSEPS + stagingDirName + DIRSEPS + str + post; - } else { - uriList += DIRSEPS + m_HGStagingDir + DIRSEPS + str + post; - } - } - - /* - * This seems to be the best place to do the blocking. If we do - * it in the source drop callback from the DnD layer, we often - * find ourselves adding the block too late; the user will (in - * GNOME, in the dest) be told that it could not find the file, - * and if you click retry, it is there, meaning the block was - * added too late). - * - * We find ourselves in this callback twice for each H->G DnD. - * We *must* always set the selection data, when called, or else - * the DnD for that context will fail, but we *must not* add the - * block twice or else things get confused. So we add a check to - * see if we are in the right state (no block yet added, and we - * are in a HG drag still, both must be true) when adding the block. - * Doing both of these addresses bug - * http://bugzilla.eng.vmware.com/show_bug.cgi?id=391661. - */ - if (!m_blockAdded && m_inHGDrag) { - m_HGGetDataInProgress = true; - m_isFileDnD = true; - AddBlock(); - } else { - Debug("%s: not calling AddBlock\n", __FUNCTION__); - } - selection_data.set(DRAG_TARGET_NAME_URI_LIST, uriList.c_str()); - Debug("%s: exit\n", __FUNCTION__); - return; - } - - if (target == DRAG_TARGET_NAME_URI_LIST && - CPClipboard_ItemExists(&m_clipboard, CPFORMAT_FILECONTENTS)) { - Debug("%s: Providing uriList [%s] for file contents DnD\n", - __FUNCTION__, m_HGFileContentsUriList.c_str()); - - selection_data.set(DRAG_TARGET_NAME_URI_LIST, - m_HGFileContentsUriList.c_str()); - return; - } - - if ((target == TARGET_NAME_STRING || - target == TARGET_NAME_TEXT_PLAIN || - target == TARGET_NAME_UTF8_STRING || - target == TARGET_NAME_COMPOUND_TEXT) && - CPClipboard_GetItem(&m_clipboard, CPFORMAT_TEXT, &buf, &sz)) { - Debug("%s: providing plain text, size %"FMTSZ"u\n", __FUNCTION__, sz); - selection_data.set(target.c_str(), (const char *)buf); - return; - } - - if ((target == TARGET_NAME_APPLICATION_RTF || - target == TARGET_NAME_TEXT_RICHTEXT) && - CPClipboard_GetItem(&m_clipboard, CPFORMAT_RTF, &buf, &sz)) { - Debug("%s: providing rtf text, size %"FMTSZ"u\n", __FUNCTION__, sz); - selection_data.set(target.c_str(), (const char *)buf); - return; - } - - /* Can not get any valid data, cancel this HG DnD. */ - Debug("%s: no valid data for HG DnD\n", __FUNCTION__); - m_DnD->SourceCancel(); - CommonResetCB(); -} - - -/** - * - * "drag_end" handler for GTK. Received by drag source. - * - * @param[in] dc drag state - */ - -void -DnDUI::GtkSourceDragEndCB(const Glib::RefPtr &dc) -{ - Debug("%s: entering dc %p, m_dc %p\n", __FUNCTION__, - dc ? dc->gobj() : NULL, m_dc ? m_dc : NULL); - - /* - * We may see a drag end for the previous DnD, but after a new - * DnD has started. If so, ignore it. - */ - if (m_dc && dc && (m_dc != dc->gobj())) { - Debug("%s: got old dc (new DnD started), ignoring\n", __FUNCTION__); - return; - } - - /* - * If we are a file DnD, don't call CommonResetCB() here, since - * we will do so in the fileCopyDoneChanged callback. - */ - if (!m_isFileDnD) { - CommonResetCB(); - } - m_inHGDrag = false; -} - -/* Gtk+ callbacks seen when we are a drag destination. */ - -/** - * - * "drag_data_received" signal handler for GTK. We requested the drag - * data earlier from some drag source on the guest; this is the response. - * - * This is for G->H DnD. - * - * @param[in] dc drag context - * @param[in] x where the drop happened - * @param[in] y where the drop happened - * @param[in] sd the received data - * @param[in] info the info that has been registered with the target in the - * target list. - * @param[in] time the timestamp at which the data was received. - */ - -void -DnDUI::GtkDestDragDataReceivedCB(const Glib::RefPtr &dc, - int x, - int y, - const Gtk::SelectionData& sd, - guint info, - guint time) -{ - Debug("%s: enter dc %p, m_dc %p\n", __FUNCTION__, - dc ? dc->gobj() : NULL, m_dc ? m_dc : NULL); - /* The GH DnD may already finish before we got response. */ - if (!m_GHDnDInProgress) { - Debug("%s: not valid\n", __FUNCTION__); - return; - } - - CPClipboard_Clear(&m_clipboard); - - /* - * Try to get data provided from the source. If we cannot get any data, - * there is no need to inform the guest of anything. If there is no data, - * reset, so that the next drag_motion callback that we see will be allowed - * to request data again. - */ - if (SetCPClipboardFromGtk(sd) == false) { - Debug("%s: Failed to set CP clipboard.\n", __FUNCTION__); - CommonResetCB(); - return; - } - if (CPClipboard_IsEmpty(&m_clipboard)) { - Debug("%s: Failed getting item.\n", __FUNCTION__); - CommonResetCB(); - return; - } - - /* - * There are two points in the DnD process at which this is called, and both - * are in response to us calling drag_data_get(). The first occurs on the - * first "drag_motion" received and is used to start a drag; at that point - * we need to provide the file list to the guest so we request the data from - * the target. The second occurs when the "drag_drop" signal is received - * and we confirm this data with the target before starting the drop. - * - * Note that we prevent against sending multiple "dragStart"s or "drop"s for - * each DnD. - */ - if (!m_GHDnDDataReceived) { - Debug("%s: Drag entering.\n", __FUNCTION__); - m_GHDnDDataReceived = true; - TargetDragEnter(); - } else { - Debug("%s: not !m_GHDnDDataReceived\n", __FUNCTION__); - } -} - - -/** - * - * "drag_drop" signal handler for GTK. Send the drop to the host (by - * way of the backdoor), then tell the host to get the files. - * - * @param[in] dc drag context - * @param[in] x x location of the drop - * @param[in] y y location of the drop - * @param[in] time timestamp for the drop - * - * @return true on success, false otherwise. - */ - -bool -DnDUI::GtkDestDragDropCB(const Glib::RefPtr &dc, - int x, - int y, - guint time) -{ - Debug("%s: enter dc %p, m_dc %p x %d y %d\n", __FUNCTION__, - (dc ? dc->gobj() : NULL), (m_dc ? m_dc : NULL), x, y); - - Glib::ustring target; - - target = m_detWnd->drag_dest_find_target(dc); - Debug("%s: calling drag_finish\n", __FUNCTION__); - dc->drag_finish(true, false, time); - - if (target == "") { - Debug("%s: No valid data on clipboard.\n", __FUNCTION__); - return false; - } - - if (CPClipboard_IsEmpty(&m_clipboard)) { - Debug("%s: No valid data on m_clipboard.\n", __FUNCTION__); - return false; - } - - return true; -} - -/* General utility functions */ - -/** - * - * Try to construct cross-platform clipboard data from selection data - * provided to us by Gtk+. - * - * @param[in] sd Gtk selection data to convert to CP clipboard data - * - * @return false on failure, true on success - */ - -bool -DnDUI::SetCPClipboardFromGtk(const Gtk::SelectionData& sd) // IN -{ - char *newPath; - char *newRelPath; - size_t newPathLen; - size_t index = 0; - DnDFileList fileList; - DynBuf buf; - uint64 totalSize = 0; - int64 size; - - const utf::string target = sd.get_target().c_str(); - - /* Try to get file list. */ - if (target == DRAG_TARGET_NAME_URI_LIST) { - /* - * Turn the uri list into two \0 delimited lists. One for full paths and - * one for just the last path component. - */ - utf::string source = sd.get_data_as_string().c_str(); - Debug("%s: Got file list: [%s]\n", __FUNCTION__, source.c_str()); - - if (sd.get_data_as_string().length() == 0) { - Debug("%s: empty file list!\n", __FUNCTION__); - return false; - } - - /* - * In gnome, before file list there may be a extra line indicating it - * is a copy or cut. - */ - if (source.length() >= 5 && source.compare(0, 5, "copy\n") == 0) { - source = source.erase(0, 5); - } - - if (source.length() >= 4 && source.compare(0, 4, "cut\n") == 0) { - source = source.erase(0, 4); - } - - while (source.length() > 0 && - (source[0] == '\n' || source[0] == '\r' || source[0] == ' ')) { - source = source.erase(0, 1); - } - - while ((newPath = DnD_UriListGetNextFile(source.c_str(), - &index, - &newPathLen)) != NULL) { - - /* - * Parse relative path. - */ - newRelPath = Str_Strrchr(newPath, DIRSEPC) + 1; // Point to char after '/' - - if ((size = File_GetSize(newPath)) >= 0) { - totalSize += size; - } else { - Debug("%s: unable to get file size for %s\n", __FUNCTION__, newPath); - } - Debug("%s: Adding newPath '%s' newRelPath '%s'\n", __FUNCTION__, - newPath, newRelPath); - fileList.AddFile(newPath, newRelPath); - free(newPath); - } - - DynBuf_Init(&buf); - fileList.SetFileSize(totalSize); - if (fileList.ToCPClipboard(&buf, false)) { - CPClipboard_SetItem(&m_clipboard, CPFORMAT_FILELIST, DynBuf_Get(&buf), - DynBuf_GetSize(&buf)); - } - DynBuf_Destroy(&buf); - return true; - } - - /* Try to get plain text. */ - if (target == TARGET_NAME_STRING || - target == TARGET_NAME_TEXT_PLAIN || - target == TARGET_NAME_UTF8_STRING || - target == TARGET_NAME_COMPOUND_TEXT) { - utf::string source = sd.get_data_as_string().c_str(); - if (source.bytes() > 0 && - source.bytes() < DNDMSG_MAX_ARGSZ && - CPClipboard_SetItem(&m_clipboard, CPFORMAT_TEXT, source.c_str(), - source.bytes() + 1)) { - Debug("%s: Got text, size %"FMTSZ"u\n", __FUNCTION__, source.bytes()); - } else { - Debug("%s: Failed to get text\n", __FUNCTION__); - return false; - } - return true; - } - - /* Try to get RTF string. */ - if (target == TARGET_NAME_APPLICATION_RTF || - target == TARGET_NAME_TEXT_RICHTEXT) { - utf::string source = sd.get_data_as_string().c_str(); - if (source.bytes() > 0 && - source.bytes() < DNDMSG_MAX_ARGSZ && - CPClipboard_SetItem(&m_clipboard, CPFORMAT_RTF, source.c_str(), - source.bytes() + 1)) { - Debug("%s: Got RTF, size %"FMTSZ"u\n", __FUNCTION__, source.bytes()); - return true; - } else { - Debug("%s: Failed to get text\n", __FUNCTION__ ); - return false; - } - } - return true; -} - - -/** - * - * Try to get last directory name from a full path name. - * - * @param[in] str pathname to process - * - * @return last dir name in the full path name if sucess, empty str otherwise - */ - -std::string -DnDUI::GetLastDirName(const std::string &str) -{ - std::string ret; - size_t start; - size_t end; - - end = str.size() - 1; - if (end >= 0 && DIRSEPC == str[end]) { - end--; - } - - if (end <= 0 || str[0] != DIRSEPC) { - return ""; - } - - start = end; - - while (str[start] != DIRSEPC) { - start--; - } - - return str.substr(start + 1, end - start); -} - - -/** - * - * Provide a substring containing the next path from the provided - * NUL-delimited string starting at the provided index. - * - * @param[in] str NUL-delimited path list - * @param[in] index current index into string - * - * @return a string with the next path or "" if there are no more paths. - */ - -utf::utf8string -DnDUI::GetNextPath(utf::utf8string& str, - size_t& index) -{ - utf::utf8string ret; - size_t start; - - if (index >= str.length()) { - return ""; - } - - for (start = index; str[index] != '\0' && index < str.length(); index++) { - /* - * Escape reserved characters according to RFC 1630. We'd use - * Escape_Do() if this wasn't a utf::string, but let's use the same table - * replacement approach. - */ - static char const Dec2Hex[] = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', - }; - - unsigned char ubyte = str[index]; - - if (ubyte == '#' || /* Fragment identifier delimiter */ - ubyte == '?' || /* Query string delimiter */ - ubyte == '*' || /* "Special significance within specific schemes" */ - ubyte == '!' || /* "Special significance within specific schemes" */ - ubyte == '%' || /* Escape character */ - ubyte >= 0x80) { /* UTF-8 encoding bytes */ - str.replace(index, 1, "%"); - str.insert(index + 1, 1, Dec2Hex[ubyte >> 4]); - str.insert(index + 2, 1, Dec2Hex[ubyte & 0xF]); - index += 2; - } - } - - ret = str.substr(start, index - start); - Debug("%s: nextpath: %s", __FUNCTION__, ret.c_str()); - index++; - return ret; -} - - -/** - * - * Issue a fake mouse move event to the detection window. Code stolen from - * DnD V2 Linux guest implementation, where it was originally defined as a - * macro. - * - * @param[in] x x-coordinate of location to move mouse to. - * @param[in] y y-coordinate of location to move mouse to. - * - * @return true on success, false on failure. - */ - -bool -DnDUI::SendFakeMouseMove(const int x, - const int y) -{ - return SendFakeXEvents(false, false, false, false, true, x, y); -} - - -/** - * - * Fake X mouse events and window movement for the provided Gtk widget. - * - * This function will optionally show the widget, move the provided widget - * to either the provided location or the current mouse position if no - * coordinates are provided, and cause a button press or release event. - * - * @param[in] showWidget whether to show Gtk widget - * @param[in] buttonEvent whether to send a button event - * @param[in] buttonPress whether to press or release mouse - * @param[in] moveWindow: whether to move our window too - * @param[in] coordsProvided whether coordinates provided - * @param[in] xCoord x coordinate - * @param[in] yCoord y coordinate - * - * @note todo this code should be implemented using GDK APIs. - * @note todo this code should be moved into the detection window class - * - * @return true on success, false on failure. - */ - -bool -DnDUI::SendFakeXEvents(const bool showWidget, - const bool buttonEvent, - const bool buttonPress, - const bool moveWindow, - const bool coordsProvided, - const int xCoord, - const int yCoord) -{ - GtkWidget *widget; - Window rootWnd; - bool ret = false; - Display *dndXDisplay; - Window dndXWindow; - Window rootReturn; - int x; - int y; - Window childReturn; - int rootXReturn; - int rootYReturn; - int winXReturn; - int winYReturn; - unsigned int maskReturn; - - x = xCoord; - y = yCoord; - - widget = GetDetWndAsWidget(); - - if (!widget) { - Debug("%s: unable to get widget\n", __FUNCTION__); - return false; - } - - dndXDisplay = GDK_WINDOW_XDISPLAY(widget->window); - dndXWindow = GDK_WINDOW_XWINDOW(widget->window); - rootWnd = RootWindow(dndXDisplay, DefaultScreen(dndXDisplay)); - - /* - * Turn on X synchronization in order to ensure that our X events occur in - * the order called. In particular, we want the window movement to occur - * before the mouse movement so that the events we are coercing do in fact - * happen. - */ - XSynchronize(dndXDisplay, True); - - if (showWidget) { - Debug("%s: showing Gtk widget\n", __FUNCTION__); - gtk_widget_show(widget); - gdk_window_show(widget->window); - } - - /* Get the current location of the mouse if coordinates weren't provided. */ - if (!coordsProvided) { - if (!XQueryPointer(dndXDisplay, rootWnd, &rootReturn, &childReturn, - &rootXReturn, &rootYReturn, &winXReturn, &winYReturn, - &maskReturn)) { - Warning("%s: XQueryPointer() returned False.\n", __FUNCTION__); - goto exit; - } - - Debug("%s: current mouse is at (%d, %d)\n", __FUNCTION__, - rootXReturn, rootYReturn); - - /* - * Position away from the edge of the window. - */ - int width = m_detWnd->GetScreenWidth(); - int height = m_detWnd->GetScreenHeight(); - bool change = false; - - x = rootXReturn; - y = rootYReturn; - - /* - * first do left and top edges. - */ - - if (x <= 5) { - x = 6; - change = true; - } - - if (y <= 5) { - y = 6; - change = true; - } - - /* - * next, move result away from right and bottom edges. - */ - if (x > width - 5) { - x = width - 6; - change = true; - } - if (y > height - 5) { - y = height - 6; - change = true; - } - - if (change) { - Debug("%s: adjusting mouse position. root %d, %d, adjusted %d, %d\n", - __FUNCTION__, rootXReturn, rootYReturn, x, y); - } - } - - if (moveWindow) { - /* - * Make sure the window is at this point and at the top (raised). The - * window is resized to be a bit larger than we would like to increase - * the likelihood that mouse events are attributed to our window -- this - * is okay since the window is invisible and hidden on cancels and DnD - * finish. - */ - XMoveResizeWindow(dndXDisplay, dndXWindow, x - 5 , y - 5, 25, 25); - XRaiseWindow(dndXDisplay, dndXWindow); - Debug("%s: move wnd to (%d, %d, %d, %d)\n", __FUNCTION__, x - 5, y - 5 , x + 25, y + 25); - } - - /* - * Generate mouse movements over the window. The second one makes ungrabs - * happen more reliably on KDE, but isn't necessary on GNOME. - */ - XTestFakeMotionEvent(dndXDisplay, -1, x, y, CurrentTime); - XTestFakeMotionEvent(dndXDisplay, -1, x + 1, y + 1, CurrentTime); - Debug("%s: move mouse to (%d, %d) and (%d, %d)\n", __FUNCTION__, x, y, x + 1, y + 1); - - if (buttonEvent) { - Debug("%s: faking left mouse button %s\n", __FUNCTION__, - buttonPress ? "press" : "release"); - XTestFakeButtonEvent(dndXDisplay, 1, buttonPress, CurrentTime); - XSync(dndXDisplay, False); - - if (!buttonPress) { - /* - * The button release simulation may be failed with some distributions - * like Ubuntu 10.4 and RHEL 6 for guest->host DnD. So first query - * mouse button status. If some button is still down, we will try - * mouse device level event simulation. For details please refer - * to bug 552807. - */ - if (!XQueryPointer(dndXDisplay, rootWnd, &rootReturn, &childReturn, - &rootXReturn, &rootYReturn, &winXReturn, - &winYReturn, &maskReturn)) { - Warning("%s: XQueryPointer returned False.\n", __FUNCTION__); - goto exit; - } - - if ((maskReturn & Button1Mask) || - (maskReturn & Button2Mask) || - (maskReturn & Button3Mask) || - (maskReturn & Button4Mask) || - (maskReturn & Button5Mask)) { - Debug("%s: XTestFakeButtonEvent was not working for button " - "release, trying XTestFakeDeviceButtonEvent now.\n", - __FUNCTION__); - ret = TryXTestFakeDeviceButtonEvent(); - } else { - Debug("%s: XTestFakeButtonEvent was working for button release.\n", - __FUNCTION__); - ret = true; - } - } else { - ret = true; - } - } - -exit: - XSynchronize(dndXDisplay, False); - return ret; -} - - -/** - * Fake X mouse events in device level. - * - * XXX The function will only be called if XTestFakeButtonEvent does not work - * for mouse button release. Later on we may only call this one for mouse - * button simulation if this is more reliable. - * - * @return true on success, false on failure. - */ - -bool -DnDUI::TryXTestFakeDeviceButtonEvent(void) -{ - XDeviceInfo *list = NULL; - XDeviceInfo *list2 = NULL; - XDevice *tdev = NULL; - XDevice *buttonDevice = NULL; - int numDevices = 0; - int i = 0; - int j = 0; - XInputClassInfo *ip = NULL; - GtkWidget *widget; - Display *dndXDisplay; - - widget = GetDetWndAsWidget(); - - if (!widget) { - Debug("%s: unable to get widget\n", __FUNCTION__); - return false; - } - - dndXDisplay = GDK_WINDOW_XDISPLAY(widget->window); - - /* First get list of all input device. */ - if (!(list = XListInputDevices (dndXDisplay, &numDevices))) { - Debug("%s: XListInputDevices failed\n", __FUNCTION__); - return false; - } else { - Debug("%s: XListInputDevices got %d devices\n", __FUNCTION__, numDevices); - } - - list2 = list; - - for (i = 0; i < numDevices; i++, list++) { - /* We only care about mouse device. */ - if (list->use != IsXExtensionPointer) { - continue; - } - - tdev = XOpenDevice(dndXDisplay, list->id); - if (!tdev) { - Debug("%s: XOpenDevice failed\n", __FUNCTION__); - continue; - } - - for (ip = tdev->classes, j = 0; j < tdev->num_classes; j++, ip++) { - if (ip->input_class == ButtonClass) { - buttonDevice = tdev; - break; - } - } - - if (buttonDevice) { - Debug("%s: calling XTestFakeDeviceButtonEvent for %s\n", - __FUNCTION__, list->name); - XTestFakeDeviceButtonEvent(dndXDisplay, buttonDevice, 1, False, - NULL, 0, CurrentTime); - buttonDevice = NULL; - } - XCloseDevice(dndXDisplay, tdev); - } - XFreeDeviceList(list2); - return true; -} - - -/** - * - * Get the GtkWidget pointer for a DragDetWnd object. The X11 Unity - * implementation requires access to the drag detection window as - * a GtkWindow pointer, which it uses to show and hide the detection - * window. This function is also called by the code that issues fake - * X events to the detection window. - * - * @return a pointer to the GtkWidget for the detection window, or NULL - * on failure. - */ - -GtkWidget * -DnDUI::GetDetWndAsWidget() -{ - GtkInvisible *window; - GtkWidget *widget = NULL; - - if (!m_detWnd) { - return NULL; - } - window = m_detWnd->gobj(); - if (window) { - widget = GTK_WIDGET(window); - } - return widget; -} - - -/** - * - * Add a block for the current H->G file transfer. Must be paired with a - * call to RemoveBlock() on finish or cancellation. - */ - -void -DnDUI::AddBlock() -{ - Debug("%s: enter\n", __FUNCTION__); - if (m_blockAdded) { - Debug("%s: block already added\n", __FUNCTION__); - return; - } - if (DnD_BlockIsReady(m_blockCtrl) && m_blockCtrl->AddBlock(m_blockCtrl->fd, m_HGStagingDir.c_str())) { - m_blockAdded = true; - Debug("%s: add block for %s.\n", __FUNCTION__, m_HGStagingDir.c_str()); - } else { - m_blockAdded = false; - Debug("%s: unable to add block dir %s.\n", __FUNCTION__, m_HGStagingDir.c_str()); - } -} - - -/** - * - * Remove block for the current H->G file transfer. Must be paired with a - * call to AddBlock(), but it will only attempt to remove block if one is - * currently in effect. - */ - -void -DnDUI::RemoveBlock() -{ - Debug("%s: enter\n", __FUNCTION__); - if (m_blockAdded && !m_HGGetDataInProgress) { - Debug("%s: removing block for %s\n", __FUNCTION__, m_HGStagingDir.c_str()); - m_blockCtrl->RemoveBlock(m_blockCtrl->fd, m_HGStagingDir.c_str()); - m_blockAdded = false; - } else { - Debug("%s: not removing block m_blockAdded %d m_HGGetDataInProgress %d\n", - __FUNCTION__, - m_blockAdded, - m_HGGetDataInProgress); - } -} - - -/** - * - * Convert a Gdk::DragAction value to its corresponding DND_DROPEFFECT. - * - * @param[in] the Gdk::DragAction value to return. - * - * @return the corresponding DND_DROPEFFECT, with DROP_UNKNOWN returned - * if no mapping is supported. - * - * @note DROP_NONE is not mapped in this function. - */ - -DND_DROPEFFECT -DnDUI::ToDropEffect(Gdk::DragAction action) -{ - DND_DROPEFFECT effect; - - switch(action) { - case Gdk::ACTION_COPY: - case Gdk::ACTION_DEFAULT: - effect = DROP_COPY; - break; - case Gdk::ACTION_MOVE: - effect = DROP_MOVE; - break; - case Gdk::ACTION_LINK: - effect = DROP_LINK; - break; - case Gdk::ACTION_PRIVATE: - case Gdk::ACTION_ASK: - default: - effect = DROP_UNKNOWN; - break; - } - return effect; -} - - -/** - * - * Try to extract file contents from m_clipboard. Write all files to a - * temporary staging directory. Construct uri list. - * - * @return true if success, false otherwise. - */ - -bool -DnDUI::WriteFileContentsToStagingDir(void) -{ - void *buf = NULL; - size_t sz = 0; - XDR xdrs; - CPFileContents fileContents; - CPFileContentsList *contentsList = NULL; - size_t nFiles = 0; - CPFileItem *fileItem = NULL; - Unicode tempDir = NULL; - size_t i = 0; - bool ret = false; - - if (!CPClipboard_GetItem(&m_clipboard, CPFORMAT_FILECONTENTS, &buf, &sz)) { - return false; - } - - /* Extract file contents from buf. */ - xdrmem_create(&xdrs, (char *)buf, sz, XDR_DECODE); - memset(&fileContents, 0, sizeof fileContents); - - if (!xdr_CPFileContents(&xdrs, &fileContents)) { - Debug("%s: xdr_CPFileContents failed.\n", __FUNCTION__); - xdr_destroy(&xdrs); - return false; - } - xdr_destroy(&xdrs); - - contentsList = fileContents.CPFileContents_u.fileContentsV1; - if (!contentsList) { - Debug("%s: invalid contentsList.\n", __FUNCTION__); - goto exit; - } - - nFiles = contentsList->fileItem.fileItem_len; - if (0 == nFiles) { - Debug("%s: invalid nFiles.\n", __FUNCTION__); - goto exit; - } - - fileItem = contentsList->fileItem.fileItem_val; - if (!fileItem) { - Debug("%s: invalid fileItem.\n", __FUNCTION__); - goto exit; - } - - /* - * Write files into a temporary staging directory. These files will be moved - * to final destination, or deleted on next reboot. - */ - tempDir = DnD_CreateStagingDirectory(); - if (!tempDir) { - Debug("%s: DnD_CreateStagingDirectory failed.\n", __FUNCTION__); - goto exit; - } - - m_HGFileContentsUriList = ""; - - for (i = 0; i < nFiles; i++) { - utf::string fileName; - utf::string filePathName; - VmTimeType createTime = -1; - VmTimeType accessTime = -1; - VmTimeType writeTime = -1; - VmTimeType attrChangeTime = -1; - - if (!fileItem[i].cpName.cpName_val || - 0 == fileItem[i].cpName.cpName_len) { - Debug("%s: invalid fileItem[%"FMTSZ"u].cpName.\n", __FUNCTION__, i); - goto exit; - } - - /* - * '\0' is used as directory separator in cross-platform name. Now turn - * '\0' in data into DIRSEPC. - * - * Note that we don't convert the final '\0' into DIRSEPC so the string - * is NUL terminated. - */ - CPNameUtil_CharReplace(fileItem[i].cpName.cpName_val, - fileItem[i].cpName.cpName_len - 1, - '\0', - DIRSEPC); - fileName = fileItem[i].cpName.cpName_val; - filePathName = tempDir; - filePathName += DIRSEPS + fileName; - - if (fileItem[i].validFlags & CP_FILE_VALID_TYPE && - CP_FILE_TYPE_DIRECTORY == fileItem[i].type) { - if (!File_CreateDirectory(filePathName.c_str())) { - goto exit; - } - Debug("%s: created directory [%s].\n", - __FUNCTION__, filePathName.c_str()); - } else if (fileItem[i].validFlags & CP_FILE_VALID_TYPE && - CP_FILE_TYPE_REGULAR == fileItem[i].type) { - FileIODescriptor file; - FileIOResult fileErr; - - FileIO_Invalidate(&file); - - fileErr = FileIO_Open(&file, - filePathName.c_str(), - FILEIO_ACCESS_WRITE, - FILEIO_OPEN_CREATE_EMPTY); - if (!FileIO_IsSuccess(fileErr)) { - goto exit; - } - - fileErr = FileIO_Write(&file, - fileItem[i].content.content_val, - fileItem[i].content.content_len, - NULL); - - FileIO_Close(&file); - Debug("%s: created file [%s].\n", - __FUNCTION__, filePathName.c_str()); - } else { - /* - * Right now only Windows can provide CPFORMAT_FILECONTENTS data. - * Symlink file is not expected. Continue with next file if the - * type is not valid. - */ - continue; - } - - /* Update file time attributes. */ - createTime = fileItem->validFlags & CP_FILE_VALID_CREATE_TIME ? - fileItem->createTime: -1; - accessTime = fileItem->validFlags & CP_FILE_VALID_ACCESS_TIME ? - fileItem->accessTime: -1; - writeTime = fileItem->validFlags & CP_FILE_VALID_WRITE_TIME ? - fileItem->writeTime: -1; - attrChangeTime = fileItem->validFlags & CP_FILE_VALID_CHANGE_TIME ? - fileItem->attrChangeTime: -1; - - if (!File_SetTimes(filePathName.c_str(), - createTime, - accessTime, - writeTime, - attrChangeTime)) { - /* Not a critical error, only log it. */ - Debug("%s: File_SetTimes failed with file [%s].\n", - __FUNCTION__, filePathName.c_str()); - } - - /* Update file permission attributes. */ - if (fileItem->validFlags & CP_FILE_VALID_PERMS) { - if (Posix_Chmod(filePathName.c_str(), - fileItem->permissions) < 0) { - /* Not a critical error, only log it. */ - Debug("%s: Posix_Chmod failed with file [%s].\n", - __FUNCTION__, filePathName.c_str()); - } - } - - /* - * If there is no DIRSEPC inside the fileName, this file/directory is a - * top level one. We only put top level name into uri list. - */ - if (fileName.find(DIRSEPS, 0) == utf::string::npos) { - m_HGFileContentsUriList += "file://" + filePathName + "\r\n"; - } - } - Debug("%s: created uri list [%s].\n", - __FUNCTION__, m_HGFileContentsUriList.c_str()); - ret = true; - -exit: - xdr_free((xdrproc_t) xdr_CPFileContents, (char *)&fileContents); - if (tempDir && !ret) { - DnD_DeleteStagingFiles(tempDir, false); - } - free(tempDir); - return ret; -} - - -/** - * - * Tell host that we are done with HG DnD initialization. - */ - -void -DnDUI::SourceDragStartDone(void) -{ - Debug("%s: enter\n", __FUNCTION__); - m_inHGDrag = true; - m_DnD->HGDragStartDone(); -} - - -/** - * Set block control member. - * - * @param[in] block control as setup by vmware-user. - */ - -void -DnDUI::SetBlockControl(DnDBlockControl *blockCtrl) -{ - m_blockCtrl = blockCtrl; -} - - -/** - * - * Got feedback from our DropSource, send it over to host. Called by - * drag motion callback. - * - * @param[in] effect feedback to send to the UI-independent DnD layer. - */ - -void -DnDUI::SourceUpdateFeedback(DND_DROPEFFECT effect) -{ - Debug("%s: entering\n", __FUNCTION__); - m_DnD->SetFeedback(effect); -} - - -/** - * - * This is triggered when user drags valid data from guest to host. Try to - * get clip data and notify host to start GH DnD. - */ - -void -DnDUI::TargetDragEnter(void) -{ - Debug("%s: entering\n", __FUNCTION__); - - /* Check if there is valid data with current detection window. */ - if (!CPClipboard_IsEmpty(&m_clipboard)) { - Debug("%s: got valid data from detWnd.\n", __FUNCTION__); - m_DnD->DragEnter(&m_clipboard); - } - - /* - * Show the window, and position it under the current mouse position. - * This is particularly important for KDE 3.5 guests. - */ - SendFakeXEvents(true, false, true, true, false, 0, 0); -} - - -/** - * - * Get Unix time in milliseconds. See man 2 gettimeofday for details. - * - * @return unix time in milliseconds. - */ - -unsigned long -DnDUI::GetTimeInMillis(void) -{ - VmTimeType atime; - - Hostinfo_GetTimeOfDay(&atime); - return((unsigned long)(atime / 1000)); -} diff --git a/open-vm-tools/vmware-user/dndUI.h b/open-vm-tools/vmware-user/dndUI.h deleted file mode 100644 index fc13566dd..000000000 --- a/open-vm-tools/vmware-user/dndUI.h +++ /dev/null @@ -1,194 +0,0 @@ -/********************************************************* - * Copyright (C) 2009 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - -/** - * @file dndUI.h - * - * Implement the methods that allow DnD between host and guest for - * protocols V3 or greater. - * - */ - -#ifndef DND_UI_H -#define DND_UI_H - -#include "stringxx/string.hh" - -extern "C" { -#include "debug.h" -#include "dnd.h" -#include "str.h" -#include "util.h" -#include "vmblock.h" -#include "dndClipboard.h" -#include "dynbuf.h" -#include "../dnd/dndFileContentsUtil.h" -#include "dynxdr.h" -#include "cpNameUtil.h" -#include "posix.h" -} - -#include "dnd.hh" -#include "dndFileList.hh" -#include "dragDetWnd.h" - -struct DblLnkLst_Links; - -/** - * The DnDUI class implements the UI portion of DnD V3 and greater - * versions of the protocol. - */ -class DnDUI -{ -public: - DnDUI(DblLnkLst_Links *eventQueue); - ~DnDUI(); - bool Init(); - void VmxDnDVersionChanged(struct RpcIn *rpcIn, - uint32 version) - {ASSERT(m_DnD); m_DnD->VmxDnDVersionChanged(rpcIn, version);} - void SetDnDAllowed(bool isDnDAllowed) - {ASSERT(m_DnD); m_DnD->SetDnDAllowed(isDnDAllowed);} - void SetBlockControl(DnDBlockControl *blockCtrl); - void SetUnityMode(Bool mode) - {m_unityMode = mode;}; - void Cancel(); - - DragDetWnd *GetFullDetWnd() {return m_detWnd;} - GtkWidget *GetDetWndAsWidget(); - -private: - - /** - * Blocking FS Helper Functions. - */ - void AddBlock(); - void RemoveBlock(); - bool TryXTestFakeDeviceButtonEvent(void); - - /** - * Callbacks from Common DnD layer. - */ - void CommonResetCB(); - void CommonUpdateMouseCB(int32 x, int32 y); - - /** - * Source functions for HG DnD. - */ - void CommonDragStartCB(const CPClipboard *clip, std::string stagingDir); - void CommonSourceDropCB(void); - - /** - * Called when HG Dnd is completed. - */ - void CommonSourceCancelCB(void); - - /** - * Called when GH DnD is completed. - */ - void CommonDestPrivateDropCB(int32 x, int32 y); - void CommonDestCancelCB(void); - - /** - * Source functions for file transfer. - */ - void CommonSourceFileCopyDoneCB(bool success, std::vector stagingDir); - - /** - * Callbacks for showing/hiding detection window. - */ - void CommonUpdateDetWndCB(bool bShow, int32 x, int32 y); - void CommonUpdateUnityDetWndCB(bool bShow, uint32 unityWndId, bool bottom); - void CommonMoveDetWndToMousePos(void); - - /** - * Gtk+ Callbacks: Drag Destination. - */ - void GtkDestDragDataReceivedCB(const Glib::RefPtr &dc, - int x, int y, const Gtk::SelectionData &sd, - guint info, guint time); - bool GtkDestDragDropCB(const Glib::RefPtr &dc, - int x, int y, guint time); - void GtkDestDragLeaveCB(const Glib::RefPtr &dc, - guint time); - bool GtkDestDragMotionCB(const Glib::RefPtr &dc, int x, - int y, guint time); - - /** - * Gtk+ Callbacks: Drag Source. - */ - void GtkSourceDragBeginCB(const Glib::RefPtr& context); - void GtkSourceDragDataGetCB(const Glib::RefPtr& context, - Gtk::SelectionData& selection_data, guint info, - guint time); - void GtkSourceDragEndCB(const Glib::RefPtr& context); - /** - * Source functions for HG DnD. Makes calls to common layer. - */ - void SourceDragStartDone(void); - void SourceUpdateFeedback(DND_DROPEFFECT effect); - - /** - * Target function for GH DnD. Makes call to common layer. - */ - void TargetDragEnter(void); - - /** - * Misc methods. - */ - bool SetCPClipboardFromGtk(const Gtk::SelectionData& sd); - std::string GetLastDirName(const std::string &str); - utf::utf8string GetNextPath(utf::utf8string &str, size_t& index); - DND_DROPEFFECT ToDropEffect(Gdk::DragAction action); - void SetTargetsAndCallbacks(); - bool SendFakeXEvents(const bool showWidget, const bool buttonEvent, - const bool buttonPress, const bool moveWindow, - const bool coordsProvided, - const int xCoord, const int yCoord); - bool SendFakeMouseMove(const int x, const int y); - bool WriteFileContentsToStagingDir(); - unsigned long GetTimeInMillis(); - - DblLnkLst_Links *m_eventQueue; - DnD *m_DnD; - std::string m_HGStagingDir; - utf::string m_HGFileContentsUriList; - DragDetWnd *m_detWnd; - CPClipboard m_clipboard; - DnDBlockControl *m_blockCtrl; - bool m_HGGetDataInProgress; - int m_HGEffect; - bool m_blockAdded; - - /* State to determine if drag motion is a drag enter. */ - bool m_GHDnDInProgress; - /* Icon updates from the guest. */ - /* Only update mouse when we have clipboard contents from the host. */ - bool m_GHDnDDataReceived; - bool m_GHDnDDropOccurred; - bool m_unityMode; - bool m_inHGDrag; - DND_DROPEFFECT m_effect; - bool m_isFileDnD; - int32 m_mousePosX; - int32 m_mousePosY; - GdkDragContext *m_dc; - unsigned long m_destDropTime; -}; - -#endif // DND_UI_H diff --git a/open-vm-tools/vmware-user/dragDetWnd.cpp b/open-vm-tools/vmware-user/dragDetWnd.cpp deleted file mode 100644 index 25f32c3f9..000000000 --- a/open-vm-tools/vmware-user/dragDetWnd.cpp +++ /dev/null @@ -1,327 +0,0 @@ -/********************************************************* - * Copyright (C) 2009 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - -/** - * @file dragDetWnd.cpp - * - * Detection window code for Linux/X11, based on Gtkmm. Includes unit test - * code. - */ - -#include "dragDetWnd.h" - -/** - * - * Constructor. - */ - -extern "C" { -#include "debug.h" -} - -#include "dndUI.h" -#include - -/** - * - * Constructor. - */ - -DragDetWnd::DragDetWnd() : - m_isVisible(false) -{ -#if defined(DETWNDDEBUG) - DebugSetAttributes(); -#endif -} - - -/** - * - * Destructor. - */ - -DragDetWnd::~DragDetWnd() -{ -} - - -/** - * Flush the X connection. - */ - -void -DragDetWnd::Flush() -{ - Glib::RefPtr gdkdisplay = Gdk::Display::get_default(); - if (gdkdisplay) { - gdkdisplay->sync(); - gdkdisplay->flush(); - } -} - - -/** - * - * Show the window. - */ - -void -DragDetWnd::Show(void) -{ - show(); - Flush(); -} - - -/** - * - * Hide the window. - */ - -void -DragDetWnd::Hide(void) -{ - hide(); - Flush(); -} - - -/** - * - * Raise the window. - */ - -void -DragDetWnd::Raise(void) -{ - Glib::RefPtr gdkwin = get_window(); - if (gdkwin) { - gdkwin->raise(); - } - Flush(); -} - - -/** - * - * Lower the window. - */ - -void -DragDetWnd::Lower(void) -{ - Glib::RefPtr gdkwin = get_window(); - if (gdkwin) { - gdkwin->lower(); - } - Flush(); -} - - -/** - * - * Get the width of the screen associated with this window. - * - * @return width of screen, in pixels. - */ - -int -DragDetWnd::GetScreenWidth(void) -{ - Glib::RefPtr gdkscreen = get_screen(); - return gdkscreen->get_width(); -} - - -/** - * - * Get the height of the screen associated with this window. - * - * @return height of screen, in pixels. - */ - -int -DragDetWnd::GetScreenHeight(void) -{ - Glib::RefPtr gdkscreen = get_screen(); - return gdkscreen->get_height(); -} - - -#if defined(DETWNDDEBUG) -/** - * - * Set default window attributes appropriate for debugging detection windows. - * - * @note This only applies to instances of DragDetWnd that are derived from - * GTK::Window. - */ - -void -DragDetWnd::DebugSetAttributes(void) -{ - set_default_size(1, 1); - set_resizable(true); - set_decorated(false); - set_type_hint(Gdk::WINDOW_TYPE_HINT_DOCK); -} -#endif - - -/** - * - * Set the geometry of the window. - * - * @param[in] x desired x-coordinate of the window. - * @param[in] y desired y-coordinate of the window. - * @param[in] width desired width of the window. - * @param[in] height desired height of the window. - */ - -void -DragDetWnd::SetGeometry(const int x, - const int y, - const int width, - const int height) -{ - Glib::RefPtr gdkwin = get_window(); - - if (gdkwin) { - gdkwin->move_resize(x, y, width, height); - Flush(); - } -} - - -/** - * - * Get the current geometry of the window. - * - * @param[out] x current x-coordinate of the window. - * @param[out] y current y-coordinate of the window. - * @param[out] width current width of the window. - * @param[out] height current height of the window. - * - * @note The current geometry may be inaccurate if retrieved too quickly - * after a change made by SetGeometry(). This is due to the realities of - * X and window managers. Some of this is mitigated by the use of flush() - * and sync() calls in SetGeometry(), but these are no guarantee. - */ - -void -DragDetWnd::GetGeometry(int &x, int &y, int &width, int &height) -{ - int dummy; - - Glib::RefPtr gdkwin = get_window(); - if (gdkwin) { - gdkwin->get_geometry(x, y, width, height, dummy); -#if defined(DETWNDTEST) - Flush(); -#endif - } -} - -/* - * Code below here is for unit tests. - */ - -#if defined(DETWNDTEST) - -/** - * - * Add a button to launch unit tests to the drag detection window. - */ - -void -DragDetWndTest::CreateTestUI() -{ - m_button.set_label("Start Unit Tests"); - add(m_button); - m_button.signal_clicked().connect(sigc::mem_fun(*this, &DragDetWndTest::RunUnitTests)); - m_button.show(); -} - - -/** - * - * Run some unit tests, then exit. Requires a main program, refer to - * bora-vmsoft/toolbox/linux/vmwareuser/detWndTest/main.cpp for an - * example. - */ - -void -DragDetWndTest::RunUnitTests() -{ - DragDetWnd testWnd; - int testCount = 0; - int failCount = 0; - -#if defined(DETWNDDEBUG) - testWnd.SetAttributes(); -#endif - testWnd.Show(); - int x, y, width, height; - testWnd.GetGeometry(x, y, width, height); - printf("Geometry is x %d y %d width %d height %d\n", x, y, width, height); - - for (int i = 10; i < 50; Gtk::Main::iteration(), i++) { - testCount++; - printf("Setting geometry to x %d y %d w %d h %d\n", i * 10, i * 10, i * 10, i * 10); - testWnd.SetGeometry(i * 10, i * 10, i * 10, i * 10); - sleep(1); - testWnd.GetGeometry(x, y, width, height); - printf("Geometry is x %d y %d width %d height %d\n", x, y, width, height); - if (x != i * 10 || y != i * 10 || width != i * 10) { - printf("FAIL x or y not correct\n"); - failCount++; - } - } - - for (int i = 49; i > 0; Gtk::Main::iteration(), i--) { - testCount++; - printf("Setting geometry to x %d y %d w %d h %d\n", i * 10, i * 10, i * 10, i * 10); - testWnd.SetGeometry(i * 10, i * 10, i * 10, i * 10); - sleep(1); - testWnd.GetGeometry(x, y, width, height); - printf("Geometry is x %d y %d width %d height %d\n", x, y, width, height); - if (x != i * 10 || y != i * 10 || width != i * 10) { - printf("FAIL width or height not correct\n"); - failCount++; - } - } - - testWnd.SetGeometry(500, 500, 300, 300); - - for (int i = 0; i < 60; Gtk::Main::iteration(), i++) { - if (i % 2) { - printf("Hide\n"); - testWnd.Hide(); - } else { - printf("Show\n"); - testWnd.Show(); - testWnd.Raise(); - } - sleep(1); - } - - printf("Done fail count %d (%.2f%%)\n", failCount, 100.0 * failCount/testCount); - Gtk::Main::quit(); -} -#endif diff --git a/open-vm-tools/vmware-user/dragDetWnd.h b/open-vm-tools/vmware-user/dragDetWnd.h deleted file mode 100644 index 6ee455f45..000000000 --- a/open-vm-tools/vmware-user/dragDetWnd.h +++ /dev/null @@ -1,75 +0,0 @@ -/********************************************************* - * Copyright (C) 2009 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - -/** - * @file dragDetWnd.h - * - * Header for the DragDetWnd class. - */ - -#ifndef DRAG_DET_WND_H -#define DRAG_DET_WND_H - -#include - -#if !defined(DETWNDTEST) -extern "C" { - #include "dnd.h" -} -#endif - -class DnDUI; - -class DragDetWnd : public Gtk::Invisible -{ -public: - DragDetWnd(); - virtual ~DragDetWnd(); - - void Show(); - void Hide(); - void Raise(); - void Lower(); - int GetScreenWidth(); - int GetScreenHeight(); - void SetGeometry(const int x, const int y, - const int width, const int height); - void GetGeometry(int &x, int &y, int &width, int &height); - void SetIsVisible(const bool isVisible) {m_isVisible = isVisible;}; - bool GetIsVisible() {return m_isVisible;}; -#if defined(DETWNDEBUG) - void DebugSetAttributes(); -#endif -private: - void Flush(); - bool m_isVisible; -}; - -#if defined(DETWNDTEST) -class DragDetWndTest : public Gtk::Window -{ -public: - void CreateTestUI(); -private: - virtual void RunUnitTests(); - Gtk::Button m_button; -}; - -#endif - -#endif // DRAG_DET_WND_H diff --git a/open-vm-tools/vmware-user/modconfig.c b/open-vm-tools/vmware-user/modconfig.c deleted file mode 100644 index 68c48506d..000000000 --- a/open-vm-tools/vmware-user/modconfig.c +++ /dev/null @@ -1,244 +0,0 @@ -/********************************************************* - * Copyright (C) 2008 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - -/* - * modconfig.c -- - * - * Handles interaction with the modconfig gui for vmware-user. - */ - -#include "vmwareuserInt.h" - -#ifdef USE_NOTIFY_DLOPEN - -#include "installerdb.h" -#include "file.h" -#include "installerdb.h" -#include "modconf.h" -#include "str.h" - - -/* - * Local functions - */ -static GtkWidget *GetMenu(void); -static void LaunchModconfig(void); -static gboolean ActivateCallback(GtkWidget *widget, Notifier *n); -static void MenuItemCallback(GObject *self, void *data); - - -/* - *---------------------------------------------------------------------------- - * - * LaunchModconfig -- - * - * Asynchronously spawn the modconfig process to rebuild kernel modules. - * - * Results: - * None. - * - * Side effects: - * The modoconfig program is launched, and modules are recompiled. - * - *---------------------------------------------------------------------------- - */ - -static void -LaunchModconfig(void) -{ - char *exePath; - char *command; - gchar *quotedExePath; - - exePath = Str_Asprintf(NULL, "%s/sbin/vmware-modconfig-wrapper", vmLibDir); - quotedExePath = g_shell_quote(exePath); - command = Str_Asprintf(NULL, "%s --icon=\"vmware-modconfig\" " - "--appname=\"VMware Tools\"", quotedExePath); - - g_spawn_command_line_async(command, NULL); - - free(command); - g_free(quotedExePath); - free(exePath); -} - - -/* - *---------------------------------------------------------------------------- - * - * ActivateCallback -- - * - * The callback invoked when the status icon is left-clicked. - * - * Results: - * TRUE if the signal is handled, FALSE otherwise. - * - * Side effects: - * Launches modconfig. - * - *---------------------------------------------------------------------------- - */ - -static gboolean -ActivateCallback(GtkWidget *widget, // IN - Notifier *n) // IN -{ - LaunchModconfig(); - return TRUE; -} - - -/* - *---------------------------------------------------------------------------- - * - * MenuItemCallback -- - * - * The callback invoked when any item on the popup context menu is clicked. - * - * Results: - * None. - * - * Side effects: - * Launches modconfig. - * - *---------------------------------------------------------------------------- - */ - -static void -MenuItemCallback(GObject *self, // IN - void *data) // IN -{ - LaunchModconfig(); -} - - -/* - *---------------------------------------------------------------------------- - * - * GetMenu -- - * - * Create the context menu for the status icon. - * - * Results: - * Returns a pointer to the created menu. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static GtkWidget * -GetMenu(void) -{ - GtkWidget *menu = gtk_menu_new(); - GtkWidget *menuItem = gtk_menu_item_new_with_label("Update Modules"); - g_signal_connect(G_OBJECT(menuItem), "activate", G_CALLBACK(MenuItemCallback), - NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuItem); - - return menu; -} - - -/* - *---------------------------------------------------------------------------- - * - * Modules_Init -- - * - * Check for kernel modules and display a notification if any are - * found missing. - * - * Results: - * TRUE on success, FALSE otherwise - * - * Side effects: - * A notification is displayed, informing the user of the missing - * modules. - * - *---------------------------------------------------------------------------- - */ - -Bool -Modules_Init(void) -{ - const char *libdir; - char *moduleListPath; - GList *modules, *modulesNotInstalled; - - if (!InstallerDB_Init("/etc/vmware-tools", TRUE)) { - return FALSE; - } - - /* - * Only do module out-of-dateness checking if we weren't installed as - * a DSP. - */ - if (InstallerDB_IsDSPInstall()) { - InstallerDB_DeInit(); - return FALSE; - } - - if (!ModConf_Init()) { - return FALSE; - } - - libdir = InstallerDB_GetLibDir(); - moduleListPath = g_build_filename(libdir, "modules/modules.xml", NULL); - modules = ModConf_GetModulesList(moduleListPath); - modulesNotInstalled = ModConf_GetModulesNotInstalled(modules); - - if (modulesNotInstalled != NULL) { - Notify_Notify(30, "Kernel modules out-of-date", - "It appears your kernel modules are not longer " - "compatible with the running kernel. Please " - "click on the icon to recompile them.", - GetMenu(), ActivateCallback); - } - - g_list_free(modulesNotInstalled); - ModConf_FreeModulesList(modules); - - return TRUE; -} - - -/* - *---------------------------------------------------------------------------- - * - * Modules_Cleanup -- - * - * Cleanup the modconf subsystem. - * - * Results: - * TRUE on success, FALSE otherwise - * - * Side effects: - * The modconf subsystem is closed. - * - *---------------------------------------------------------------------------- - */ - -void -Modules_Cleanup(void) -{ - ModConf_DeInit(); - InstallerDB_DeInit(); -} - -#endif /* USE_NOTIFY_DLOPEN */ diff --git a/open-vm-tools/vmware-user/notify.c b/open-vm-tools/vmware-user/notify.c deleted file mode 100644 index 095e68f51..000000000 --- a/open-vm-tools/vmware-user/notify.c +++ /dev/null @@ -1,315 +0,0 @@ -/********************************************************* - * Copyright (C) 2008 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - -/* - * notify.c -- - * - * Handles the system tray notifications for vmware-user. - */ - -#include "vmware.h" -#include "vmwareuserInt.h" - -#ifdef USE_NOTIFY - -#include "conf.h" -#include "debug.h" -#include "str.h" - -#if defined(USE_NOTIFY_SO) -#include -#include -#include -#elif defined(USE_NOTIFY_DLOPEN) -#include -#include -#endif - - -/* - * Local symbols - */ -const char *vmLibDir = NULL; - -#ifdef USE_NOTIFY_DLOPEN -static gboolean (*notify_init)(const char *app_name) = NULL; -static void (*notify_uninit)(void) = NULL; -static NotifyNotification *(*notify_notification_new_with_status_icon) - (const gchar *summary, const gchar *body, - const gchar *icon, GtkStatusIcon *status_icon) = NULL; -static gboolean (*notify_notification_show)(NotifyNotification *notification, - GError **error) = NULL; -void (*notify_notification_set_timeout)(NotifyNotification *notification, - gint timeout) = NULL; - -static Bool LoadLibNotify(void); -static Bool UnloadLibNotify(void); - -static void *libNotifyHandle = NULL; -static Bool initialized = FALSE; - - -/* - *---------------------------------------------------------------------------- - * - * LoadLibNotify -- - * - * Dynamically load required symbols from libnotify. We only do this - * when building inside the VMware tree; when shipping open-vm-tools, - * we can just let the linker do the work, since we have the libraries - * available at build-time. - * - * Results: - * TRUE on success, FALSE otherwise - * - * Side effects: - * libnotify is loaded, and symbols populated. - * - *---------------------------------------------------------------------------- - */ - -static Bool -LoadLibNotify(void) -{ - int i; - - libNotifyHandle = dlopen("libnotify.so.1", RTLD_LAZY); - - if (!libNotifyHandle) { - return FALSE; - } - - /* - * The list of symbols we want to dynamically load from libnotify. It - * must be NULL-terminated. To load additional symbols, be sure to - * define them above, with file-level scope, and then add them to this - * list. - */ - { - struct FuncEntry - { - void **funcPtr; - const char *symName; - } vtable[] = { - { (void **) ¬ify_init, "notify_init" }, - { (void **) ¬ify_uninit, "notify_uninit" }, - { (void **) ¬ify_notification_show, "notify_notification_show" }, - { (void **) ¬ify_notification_new_with_status_icon, - "notify_notification_new_with_status_icon" }, - { (void **) ¬ify_notification_set_timeout, - "notify_notification_set_timeout" }, - { NULL, NULL } - }; - - /* - * Load each of the above symbols from libnotify, checking to make sure - * that they exist. - */ - for (i = 0; vtable[i].funcPtr != NULL; i++) { - *(vtable[i].funcPtr) = dlsym(libNotifyHandle, vtable[i].symName); - if ( *(vtable[i].funcPtr) == NULL) { - Debug("Could not find %s in libnotify\n", vtable[i].symName); - UnloadLibNotify(); - return FALSE; - } - } - } - - return TRUE; -} - - -/* - *---------------------------------------------------------------------------- - * - * UnloadLibNotify -- - * - * Decrement the reference count for libnotify. We only do this when - * building inside the VMware tree; when shipping open-vm-tools, we can - * just let the linker do the work, since we have the libraries - * available at build-time. - * - * Results: - * TRUE on success, FALSE otherwise - * - * Side effects: - * libnotify is unloaded - * - *---------------------------------------------------------------------------- - */ - -static Bool -UnloadLibNotify(void) -{ - return dlclose(libNotifyHandle) == 0; -} -#endif /* USE_NOTIFY_DLOPEN */ - - -/* - *---------------------------------------------------------------------------- - * - * Notify_Init -- - * - * Initializes the notification system. - * - * Results: - * TRUE on success, FALSE otherwise - * - * Side effects: - * Notification system is initialized. - * - *---------------------------------------------------------------------------- - */ - -Bool -Notify_Init(GuestApp_Dict *confDict) // IN: Configuration dictionary -{ -#ifdef USE_NOTIFY_DLOPEN - if (LoadLibNotify() == FALSE) { - return FALSE; - } -#endif - - vmLibDir = GuestApp_GetDictEntry(confDict, CONFNAME_LIBDIR); - initialized = notify_init("vmware-user"); - - return initialized; -} - - -/* - *---------------------------------------------------------------------------- - * - * Notify_Cleanup -- - * - * Clean up the notification system. - * - * Results: - * None. - * - * Side effects: - * Notification system is deinitialized. - * - *---------------------------------------------------------------------------- - */ - -void -Notify_Cleanup(void) -{ - initialized = FALSE; - notify_uninit(); - -#ifdef USE_NOTIFY_DLOPEN - UnloadLibNotify(); -#endif -} - - -/* - *---------------------------------------------------------------------------- - * - * PopupCallback -- - * - * The callback invoked when the status icon is right-clicked. - * - * Results: - * TRUE if the signal is handled, FALSE otherwise. - * - * Side effects: - * Displays the popup menu for the icon. - * - *---------------------------------------------------------------------------- - */ - -static gboolean -PopupCallback(GtkStatusIcon *statusIcon, // IN - guint button, // IN - guint activateTime, // IN - Notifier *n) // IN -{ - gtk_menu_set_screen(GTK_MENU(n->menu), - gtk_status_icon_get_screen(n->statusIcon)); - gtk_menu_popup(GTK_MENU(n->menu), NULL, NULL, - gtk_status_icon_position_menu, n->statusIcon, button, - activateTime); - return TRUE; -} - - -/* - *---------------------------------------------------------------------------- - * - * Notify_Notify -- - * - * Create and display the notification icon with the given message. - * - * Results: - * TRUE if successful, FALSE otherwise. - * - * Side effects: - * Notification is displayed. - * - *---------------------------------------------------------------------------- - */ - -Bool -Notify_Notify(int secs, // IN: Number of seconds to display - const char *shortMsg, // IN: Short summary message - const char *longMsg, // IN: Longer detailed message - GtkWidget *menu, // IN: Context menu - gboolean (*activateCallback)(GtkWidget *, Notifier *)) - // IN: The left-click callback -{ - char *iconPath; - Notifier *n; - - if (!initialized) { - return FALSE; - } - - n = g_new0(Notifier, 1); - iconPath = Str_Asprintf(NULL, "%s/share/icons/vmware.png", vmLibDir); - n->statusIcon = gtk_status_icon_new_from_file(iconPath); - gtk_status_icon_set_tooltip(n->statusIcon, shortMsg); - gtk_status_icon_set_visible(n->statusIcon, TRUE); - free(iconPath); - - /* - * Display the notification for secs seconds. - */ - n->notification = notify_notification_new_with_status_icon(shortMsg, longMsg, - NULL, n->statusIcon); - notify_notification_set_timeout(n->notification, secs * 1000); - notify_notification_show(n->notification, NULL); - - /* - * Connect the click and right-click signals. - */ - g_signal_connect(G_OBJECT(n->statusIcon), "activate", - G_CALLBACK(activateCallback), n); - g_signal_connect(G_OBJECT(n->statusIcon), "popup-menu", - G_CALLBACK(PopupCallback), n); - - n->menu = menu; - gtk_widget_show_all(n->menu); - - return TRUE; -} - -#endif diff --git a/open-vm-tools/vmware-user/pointer.c b/open-vm-tools/vmware-user/pointer.c deleted file mode 100644 index 96da76404..000000000 --- a/open-vm-tools/vmware-user/pointer.c +++ /dev/null @@ -1,245 +0,0 @@ -/********************************************************* - * Copyright (C) 2005 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - -/* - * pointer.c -- - * - * Set of functions for pointer (mouse) grab/ungrab. - */ - -#include "vmwareuserInt.h" -#include -#include - -#include "vm_assert.h" -#include "debug.h" -#include "str.h" -#include "strutil.h" -#include "guestApp.h" -#include "eventManager.h" -#include "vmware/guestrpc/tclodefs.h" - -static GuestAppAbsoluteMouseState absoluteMouseState = GUESTAPP_ABSMOUSE_UNKNOWN; -static Bool mouseIsGrabbed; -static uint8 gHostClipboardTries = 0; - -/* - * Forward Declarations - */ -void PointerGrabbed(void); -void PointerUngrabbed(void); -void PointerGetXCursorPos(int *x, int *y); -static Bool PointerUpdatePointerLoop(void* clientData); - -/* - *----------------------------------------------------------------------------- - * - * PointerGetXCursorPos -- - * - * Return the position in pixels of the X (mouse) pointer in the root - * window. - * - * Results: - * x and y coordinates. - * - * Side effects: - * None. - *----------------------------------------------------------------------------- - */ - -void -PointerGetXCursorPos(int *rootX, int *rootY) -{ - Window rootWin; - Window childWin; - int x; - int y; - unsigned int mask; - - XQueryPointer(gXDisplay, gXRoot, &rootWin, &childWin, rootX, rootY, &x, &y, &mask); -} - - -/* - *----------------------------------------------------------------------------- - * - * PointerSetXCursorPos - * - * Set the position in pixels of the X (mouse) pointer in the root window - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -void -PointerSetXCursorPos(int x, int y) -{ - XWarpPointer(gXDisplay, None, gXRoot, 0, 0, 0, 0, x, y); -} - - -/* - *----------------------------------------------------------------------------- - * - * PointerGrabbed -- - * - * Called when the pointer's state switches from released to grabbed. - * We warp the cursor to whatever position the vmx tells us, and then - * setup the loop which attempts to get the host clipboard. - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -void -PointerGrabbed() -{ - short hostPosX; - short hostPosY; - - GuestApp_GetPos(&hostPosX, &hostPosY); - - PointerSetXCursorPos(hostPosX, hostPosY); - gHostClipboardTries = 9; -} - - -/* - *----------------------------------------------------------------------------- - * - * PointerUngrabbed -- - * - * Called when the pointer's state switches from grabbed to release. - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ -void -PointerUngrabbed(void) -{ - CopyPaste_RequestSelection(); -} - - -/* - *----------------------------------------------------------------------------- - * - * PointerUpdatePointerLoop -- - * - * Event Manager function for tracking the mouse/pointer/clipboard state. - * Manage grabbed/ungrab state based on x/y data from backdoor. On the - * transition to grabbed, call PointerHasBeenGrabbed(). While grabbed, - * send guest pointer coordinates thru the backdoor. Also, make several - * attempts to get the host clipboard from the backdoor. When changing - * to ungrabbed, call PointerHasBeenUngrabbed, which will push our - * clipboard thru the backdoor. While ungrabbed, don't do a thing. - * - * This function is queued in Event Manager only when vmx doesn't support - * RPC copy/paste because newer vmx initiates copy/paste from UI through - * RPC, and doesn't need cursor grab/ungrab state to start copy/paste. - * - * Results: - * TRUE. - * - * Side effects: - * Lots. The vmx's notion of guest cursor position could change, the - * vmx's clipboard could change, and the guest's clipboard could change. - * - *----------------------------------------------------------------------------- - */ - -static Bool -PointerUpdatePointerLoop(void* clientData) // IN: unused -{ - int16 hostX, hostY; - int guestX, guestY; - - GuestApp_GetPos(&hostX, &hostY); - if (mouseIsGrabbed) { - if (hostX == UNGRABBED_POS) { - /* We transitioned from grabbed to ungrabbed */ - mouseIsGrabbed = FALSE; - PointerUngrabbed(); - } else { - PointerGetXCursorPos(&guestX, &guestY); - if (hostX != guestX || hostY != guestY) { - GuestApp_SetPos(guestX,guestY); - } - if (gHostClipboardTries-- > 0) { - if (gHostClipboardTries < 6 && - CopyPaste_GetBackdoorSelections()) { - gHostClipboardTries = 0; - } - } - } - } else { - if (hostX != UNGRABBED_POS) { - mouseIsGrabbed = TRUE; - PointerGrabbed(); - } - - } - - if (!CopyPaste_IsRpcCPSupported() || - (absoluteMouseState == GUESTAPP_ABSMOUSE_UNAVAILABLE)) { - EventManager_Add(gEventQueue, POINTER_POLL_TIME, PointerUpdatePointerLoop, - clientData); - } - return TRUE; -} - - -/* - *----------------------------------------------------------------------------- - * - * Pointer_Register -- - * - * Initialize Pointer. - * - * Results: - * Always TRUE. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -Pointer_Register(GtkWidget* mainWnd) -{ - absoluteMouseState = GuestApp_GetAbsoluteMouseState(); - PointerUpdatePointerLoop(mainWnd); - mouseIsGrabbed = FALSE; - return TRUE; -} diff --git a/open-vm-tools/vmware-user/vmware-user.cpp b/open-vm-tools/vmware-user/vmware-user.cpp deleted file mode 100644 index 98cabd1ce..000000000 --- a/open-vm-tools/vmware-user/vmware-user.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/********************************************************* - * Copyright (C) 2009 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - -/** - * @file vmware-user.cpp - * - * The linux vmware-user app. It's a hidden window app that is supposed - * to run on session start. It handles tools features which we want - * active all the time, but don't want to impose a visible window on the - * user. - */ - -/* - *----------------------------------------------------------------------------- - * - * main -- - * - * This is main - * - * Results: - * Returns either EXIT_SUCCESS or EXIT_FAILURE appropriately. - * - * Side effects: - * The linux toolbox ui will run and do a variety of tricks for your - * amusement. - * - *----------------------------------------------------------------------------- - */ - -int -main(int argc, // IN - char *argv[], // IN - char *envp[]) // IN -{ - return 0; -} diff --git a/open-vm-tools/vmware-user/vmwareuserInt.h b/open-vm-tools/vmware-user/vmwareuserInt.h deleted file mode 100644 index af35eee41..000000000 --- a/open-vm-tools/vmware-user/vmwareuserInt.h +++ /dev/null @@ -1,125 +0,0 @@ -/********************************************************* - * Copyright (C) 2005 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - -/* - * vmwareuserInt.h -- - * - * Common defines used by vmwareuser - */ -#ifndef _VMWAREUSER_INT_H_ -# define _VMWAREUSER_INT_H_ - -#include -#include -#ifndef NO_MULTIMON -#include -#endif -#include -#include -#undef Bool -#include "vm_basic_types.h" -#include "rpcout.h" -#include "rpcin.h" -#include "dnd.h" - -#include "guestApp.h" - -/* - * These must be the same as the minimum values used by the lots_of_modlines() - * function in config.pl - */ -#define RESOLUTION_MIN_WIDTH 100 -#define RESOLUTION_MIN_HEIGHT 100 - -#define RPCIN_POLL_TIME 10 /* in 1/1000ths of a second */ -#define POINTER_POLL_TIME 15 /* in 1/1000ths of a second */ -#define UNGRABBED_POS (-100) -#define DEBUG_PREFIX "vmusr" - -#define FCP_FILE_TRANSFER_NOT_YET 0 -#define FCP_FILE_TRANSFERRING 1 -#define FCP_FILE_TRANSFERRED 2 - -Bool DnD_Register(GtkWidget *hgWnd, GtkWidget *ghWnd); -Bool DnD_RegisterCapability(void); -void DnD_Unregister(GtkWidget *hgWnd, GtkWidget *ghWnd); -uint32 DnD_GetVmxDnDVersion(void); -int DnD_GetNewFileRoot(char *fileRoot, int bufSize); -void DnD_OnReset(GtkWidget *hgWnd, GtkWidget *gHWnd); -Bool DnD_InProgress(void); -void DnD_SetMode(Bool unity); - -Bool CopyPaste_Register(GtkWidget* mainWnd); -Bool CopyPaste_RegisterCapability(void); -int32 CopyPaste_GetVmxCopyPasteVersion(void); -void CopyPaste_RequestSelection(void); -Bool CopyPaste_GetBackdoorSelections(void); -void CopyPaste_Unregister(GtkWidget* mainWnd); -Bool CopyPaste_GHFileListGetNext(char **fileName, size_t *fileNameSize); -void CopyPaste_OnReset(void); -Bool CopyPaste_InProgress(void); -Bool CopyPaste_IsRpcCPSupported(void); - -Bool Pointer_Register(GtkWidget* mainWnd); - -#if defined(USING_AUTOCONF) && defined(HAVE_LIBNOTIFY) -#if defined(USE_NOTIFY_DLOPEN) -#error "USE_NOTIFY_SO and USE_NOTIFY_DLOPEN cannot be simultaneously defined" -#endif - -#define USE_NOTIFY_SO -#endif - -#if defined(USE_NOTIFY_SO) || defined(USE_NOTIFY_DLOPEN) -#define USE_NOTIFY -#endif - -#ifdef USE_NOTIFY -#ifdef USE_NOTIFY_DLOPEN -struct NotifyNotification; -typedef struct NotifyNotification NotifyNotification; -#endif - -typedef struct -{ - GtkStatusIcon *statusIcon; - NotifyNotification *notification; - GtkWidget *menu; -} Notifier; -extern const char *vmLibDir; - -Bool Notify_Init(GuestApp_Dict *confDict); -void Notify_Cleanup(void); -Bool Notify_Notify(int secs, const char *shortMsg, const char *longMsg, - GtkWidget *menu, - gboolean (*callback)(GtkWidget *, Notifier *)); - -#ifdef USE_NOTIFY_DLOPEN -Bool Modules_Init(void); -void Modules_Cleanup(void); -#endif -#endif - -extern RpcIn *gRpcIn; -extern Display *gXDisplay; -extern Window gXRoot; -extern DblLnkLst_Links *gEventQueue; -extern GtkWidget *gUserMainWidget; -extern DnDBlockControl gBlockCtrl; - -#endif // _VMWAREUSER_INT_H_ diff --git a/open-vm-tools/vmware-user/vmwareuser_version.h b/open-vm-tools/vmware-user/vmwareuser_version.h deleted file mode 100644 index 849211538..000000000 --- a/open-vm-tools/vmware-user/vmwareuser_version.h +++ /dev/null @@ -1,39 +0,0 @@ -/********************************************************* - * Copyright (C) 2007 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - -/* - * vmwareuser_version.h -- - * - * Version definitions for vmware-user (non-Windows). - */ - -#ifndef _VMWAREUSER_VERSION_H_ -#define _VMWAREUSER_VERSION_H_ - -/* - * This component's version is coupled with Tools versioning. The effect - * is that the version increments with each build, and with each Tools - * version bump. If and when it becomes necessary to version the component - * manually, make sure that the version is bumped any time the component or - * its dependencies are changed. - */ -#include "vm_tools_version.h" -#define VMWAREUSER_VERSION_COMMAS TOOLS_VERSION_EXT_CURRENT_CSV -#define VMWAREUSER_VERSION_STRING TOOLS_VERSION_EXT_CURRENT_STR - -#endif /* _VMWAREUSER_VERSION_H_ */