# 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
SUBDIRS += scripts
SUBDIRS += services
SUBDIRS += toolbox
+if HAVE_X11
+ SUBDIRS += vmware-user-suid-wrapper
+endif
if HAVE_FUSE
SUBDIRS += vmblock-fuse
endif
services/plugins/vix/Makefile \
services/plugins/vixUser/Makefile \
services/plugins/vmbackup/Makefile \
- vmware-user/Makefile \
vmware-user-suid-wrapper/Makefile \
toolbox/Makefile \
hgfsclient/Makefile \
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
+++ /dev/null
-/*********************************************************
- * 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 <sys/param.h>
-
-#include <fcntl.h>
-#include <string.h>
-#include <strings.h>
-
-#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;
-}
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;
* 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);
/*
* if CompatExec fails.
*/
Error("could not execute %s: %s\n", path, strerror(errno));
- exit(EXIT_FAILURE);
+ _exit(EXIT_FAILURE);
}
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
* 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
*/
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
+++ /dev/null
- 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.
-
- <one line to give the library's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- 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.
-
- <signature of Ty Coon>, 1 April 1990
- Ty Coon, President of Vice
-
-That's all there is to it!
+++ /dev/null
-################################################################################
-### 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
+++ /dev/null
-/*********************************************************
- * 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 <stdlib.h>
-#include <string.h>
-#include <sys/time.h>
-#include <unistd.h>
-#include <errno.h>
-
-#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:
- * <file name in guest format><NUL><filename in CPName format>
- */
- 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);
- }
-}
+++ /dev/null
-/*********************************************************
- * 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);
- }
- }
-}
-
+++ /dev/null
-/*********************************************************
- * 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 <gtk/gtk.h>
-
-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
+++ /dev/null
-/*********************************************************
- * 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 <sys/time.h>
-#include <time.h>
-#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<Gtk::Clipboard> 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<utf::string>(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<Gtk::Clipboard> 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<Gtk::Clipboard> 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<Gdk::Pixbuf> 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<utf::string>::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<Gtk::Clipboard> refClipboard =
- Gtk::Clipboard::get(GDK_SELECTION_CLIPBOARD);
- Glib::RefPtr<Gtk::Clipboard> 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<Gtk::TargetEntry> 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<Gdk::PixbufLoader> 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. */
-}
-
+++ /dev/null
-/*********************************************************
- * 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 <gtkmm.h>
-#include <list>
-#include <vector>
-
-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<Gtk::TargetEntry> 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<utf::string> mHGFileContentsList;
- bool mFileTransferDone;
- bool mBlockAdded;
- DnDBlockControl *mBlockCtrl;
- bool mInited;
-};
-
-#endif // COPYPASTE_UI_H
+++ /dev/null
-/*********************************************************
- * 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 <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-#if defined(_WIN32) && defined(_MSC_VER)
-# include <windows.h>
-#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);
-}
+++ /dev/null
-/*********************************************************
- * 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 <string.h>
-#include <stdlib.h>
-#include <X11/extensions/XTest.h> /* for XTest*() */
-#include <X11/keysym.h> /* for XK_Escape */
-#include <X11/Xatom.h> /* 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:
- * <file name in guest format><NUL><filename in CPName format>
- */
- 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.
- *
- * <rpcCmd> <file root in local format><NUL><file root in CPName>
- */
- 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;
-}
+++ /dev/null
-/*********************************************************
- * 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 <gtk/gtk.h>
-#include <X11/extensions/XTest.h> /* 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<Gtk::TargetEntry> 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<Gtk::TargetList> 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<Gtk::TargetEntry>());
-
- 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<int>(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<uint8> 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<Gdk::DragContext> &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<std::string, Gdk::AtomStringTraits> targets(
- dc->gobj()->targets, Glib::OWNERSHIP_NONE);
-
- std::list<Glib::ustring> as = targets;
- std::list<Glib::ustring>::iterator result;
- char *pid;
- pid = Str_Asprintf(NULL, "guest-dnd-target %d", static_cast<int>(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<Gdk::DragContext> &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<Gdk::DragContext>& 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<Gdk::DragContext> &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<Gdk::DragContext> &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<Gdk::DragContext> &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<Gdk::DragContext> &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));
-}
+++ /dev/null
-/*********************************************************
- * 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<uint8> 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<Gdk::DragContext> &dc,
- int x, int y, const Gtk::SelectionData &sd,
- guint info, guint time);
- bool GtkDestDragDropCB(const Glib::RefPtr<Gdk::DragContext> &dc,
- int x, int y, guint time);
- void GtkDestDragLeaveCB(const Glib::RefPtr<Gdk::DragContext> &dc,
- guint time);
- bool GtkDestDragMotionCB(const Glib::RefPtr<Gdk::DragContext> &dc, int x,
- int y, guint time);
-
- /**
- * Gtk+ Callbacks: Drag Source.
- */
- void GtkSourceDragBeginCB(const Glib::RefPtr<Gdk::DragContext>& context);
- void GtkSourceDragDataGetCB(const Glib::RefPtr<Gdk::DragContext>& context,
- Gtk::SelectionData& selection_data, guint info,
- guint time);
- void GtkSourceDragEndCB(const Glib::RefPtr<Gdk::DragContext>& 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
+++ /dev/null
-/*********************************************************
- * 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 <list>
-
-/**
- *
- * Constructor.
- */
-
-DragDetWnd::DragDetWnd() :
- m_isVisible(false)
-{
-#if defined(DETWNDDEBUG)
- DebugSetAttributes();
-#endif
-}
-
-
-/**
- *
- * Destructor.
- */
-
-DragDetWnd::~DragDetWnd()
-{
-}
-
-
-/**
- * Flush the X connection.
- */
-
-void
-DragDetWnd::Flush()
-{
- Glib::RefPtr<Gdk::Display> 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<Gdk::Window> gdkwin = get_window();
- if (gdkwin) {
- gdkwin->raise();
- }
- Flush();
-}
-
-
-/**
- *
- * Lower the window.
- */
-
-void
-DragDetWnd::Lower(void)
-{
- Glib::RefPtr<Gdk::Window> 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<Gdk::Screen> 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<Gdk::Screen> 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<Gdk::Window> 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<Gdk::Window> 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
+++ /dev/null
-/*********************************************************
- * 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 <gtkmm.h>
-
-#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
+++ /dev/null
-/*********************************************************
- * 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 */
+++ /dev/null
-/*********************************************************
- * 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 <libnotify/notify.h>
-#include <libnotify/notification.h>
-#include <libnotify/NotifyNotification.h>
-#elif defined(USE_NOTIFY_DLOPEN)
-#include <dlfcn.h>
-#include <stdlib.h>
-#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
+++ /dev/null
-/*********************************************************
- * 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 <stdlib.h>
-#include <string.h>
-
-#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;
-}
+++ /dev/null
-/*********************************************************
- * 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;
-}
+++ /dev/null
-/*********************************************************
- * 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 <X11/Xlib.h>
-#include <X11/extensions/Xrandr.h>
-#ifndef NO_MULTIMON
-#include <X11/extensions/Xinerama.h>
-#endif
-#include <gtk/gtk.h>
-#include <gdk/gdkx.h>
-#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_
+++ /dev/null
-/*********************************************************
- * 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_ */