diff --git a/CMakeLists.txt b/CMakeLists.txt index fba2dff66e..5022dd05c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,8 +35,8 @@ endif() option(INSTALL_SYSTEMD_UNITS "Install TigerVNC systemd units" ON) -if(MSVC) - message(FATAL_ERROR "TigerVNC cannot be built with Visual Studio. Please use MinGW") +if(MSVC AND NOT ALLOW_MSVC_EXPERIMENTAL) + message(FATAL_ERROR "TigerVNC cannot be built with Visual Studio. Please use MinGW (or pass -DALLOW_MSVC_EXPERIMENTAL=ON to try anyway)") endif() if(NOT BUILD_TIMESTAMP) @@ -67,27 +67,40 @@ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG") # Enable debug friendly optimizations for debug builds -set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Og") -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og") - -# Make sure we get a sane C and C++ version -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") - -# Tell the compiler to be stringent -add_compile_definitions(_FORTIFY_SOURCE=2) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat=2 -Wvla") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wformat=2 -Wvla") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wzero-as-null-pointer-constant") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-override") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wshadow") -# Make sure we catch these issues whilst developing -set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Werror") -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror") -# clang doesn't support format_arg, which breaks this warning -if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format-nonliteral -Wno-format-security") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format-nonliteral -Wno-format-security") +# (GCC/Clang-only spelling; MSVC's own /RTC1 default conflicts with it — see +# "D8016: '/RTC1' and '/Og' command-line options are incompatible") +if(NOT MSVC) + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Og") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og") +else() + # cl.exe rejects every GNU spelling above, so MSVC would otherwise get + # no standard flag at all and fall back to its default -- which is too + # old for the inline variables in SSecurityRSAAES.cxx. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17") +endif() + +# These are GCC/Clang-only flag spellings; MSVC's cl.exe rejects them outright +# (e.g. "D8021: invalid numeric argument '/Wextra'"), so keep the whole GNU-style +# stringency stanza out of MSVC builds and rely on CMake's own MSVC defaults. +if(NOT MSVC) + # Make sure we get a sane C and C++ version + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") + # Tell the compiler to be stringent + add_compile_definitions(_FORTIFY_SOURCE=2) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat=2 -Wvla") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wformat=2 -Wvla") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wzero-as-null-pointer-constant") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-override") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wshadow") + # Make sure we catch these issues whilst developing + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Werror") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror") + # clang doesn't support format_arg, which breaks this warning + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format-nonliteral -Wno-format-security") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format-nonliteral -Wno-format-security") + endif() endif() option(ENABLE_ASAN "Enable address sanitizer support" OFF) @@ -103,8 +116,12 @@ if(ENABLE_TSAN AND NOT WIN32 AND NOT APPLE AND CMAKE_SIZEOF_VOID_P MATCHES 8) endif() if(MSVC) - # undef min and max macro - target_compile_definitions(rfb PRIVATE NOMINMAX) + # undef min and max macro -- global, not per-target: the "rfb" target + # doesn't exist yet at this point in the file (add_subdirectory(common) + # runs later), so target_compile_definitions(rfb ...) here always failed; + # this MSVC branch was untestable before the hard MSVC gate above was + # relaxed, so the ordering bug was never caught. + add_compile_definitions(NOMINMAX) endif() if(NOT DEFINED BUILD_WINVNC) @@ -121,6 +138,12 @@ endif() # Minimum version is Windows 7 if(WIN32) add_definitions(-D_WIN32_WINNT=0x0601) + # Without this, pulls in the legacy , which then + # conflicts with wherever both end up included in the same + # translation unit regardless of include order (duplicate struct/function + # definitions with "different linkage"). MinGW builds never hit this since + # mingw's own already defines this by default. + add_definitions(-DWIN32_LEAN_AND_MEAN) endif() # Legacy macros (macOS 10.12 and older) conflict with our code @@ -185,10 +208,6 @@ trioption(ENABLE_H264 "Enable H.264 RFB encoding") if(ENABLE_H264) if(WIN32) set(HAVE_H264 1) - - set(CMAKE_REQUIRED_LIBRARIES ole32 mfplat mfuuid wmcodecdspuuid) - check_variable_exists(CLSID_VideoProcessorMFT HAVE_VIDEO_PROCESSOR_MFT) - set(CMAKE_REQUIRED_LIBRARIES) else() if(ENABLE_H264 STREQUAL "AUTO") find_package(AVCodec) @@ -289,7 +308,9 @@ if(BUILD_VIEWER) endif() if(FLTK_FOUND) - set(CMAKE_REQUIRED_FLAGS "-Wno-error") + if(NOT MSVC) + set(CMAKE_REQUIRED_FLAGS "-Wno-error") + endif() set(CMAKE_REQUIRED_INCLUDES ${FLTK_INCLUDE_DIR}) set(CMAKE_REQUIRED_LIBRARIES ${FLTK_LIBRARIES}) @@ -439,12 +460,15 @@ include(cmake/StaticBuild.cmake) add_subdirectory(common) -if(WIN32) - add_subdirectory(win) -else() - # No interest in building x related parts on Apple - if(NOT APPLE) - add_subdirectory(unix) +option(BUILD_SERVER "Build the platform-specific VNC server components" ON) +if(BUILD_SERVER) + if(WIN32) + add_subdirectory(win) + else() + # No interest in building x related parts on Apple + if(NOT APPLE) + add_subdirectory(unix) + endif() endif() endif() diff --git a/common/core/Configuration.cxx b/common/core/Configuration.cxx index 234e112f96..571162f006 100644 --- a/common/core/Configuration.cxx +++ b/common/core/Configuration.cxx @@ -36,6 +36,7 @@ #include #include #include +#include #include #include diff --git a/common/core/LogWriter.cxx b/common/core/LogWriter.cxx index 4490c874d7..7b0e17a665 100644 --- a/common/core/LogWriter.cxx +++ b/common/core/LogWriter.cxx @@ -26,6 +26,7 @@ #include #include +#include #include #include #include diff --git a/common/core/LogWriter.h b/common/core/LogWriter.h index b247bbec46..646b050e6e 100644 --- a/common/core/LogWriter.h +++ b/common/core/LogWriter.h @@ -25,6 +25,7 @@ #include #include +#include // Each log writer instance has a unique textual name, // and is attached to a particular Log instance and @@ -32,13 +33,13 @@ #define DEF_LOGFUNCTION(name, level) \ inline void v##name(const char* fmt, va_list ap) \ - __attribute__((__format__ (__printf__, 2, 0))) \ + CORE_FORMAT_PRINTF(2, 0) \ { \ if (m_log && (level <= m_level)) \ m_log->write(level, m_name, fmt, ap); \ } \ inline void name(const char* fmt, ...) \ - __attribute__((__format__ (__printf__, 2, 3))) \ + CORE_FORMAT_PRINTF(2, 3) \ { \ if (m_log && (level <= m_level)) { \ va_list ap; va_start(ap, fmt); \ @@ -61,7 +62,7 @@ namespace core { int getLevel(void) { return m_level; } inline void write(int level, const char* format, ...) - __attribute__((__format__ (__printf__, 3, 4))) + CORE_FORMAT_PRINTF(3, 4) { if (m_log && (level <= m_level)) { va_list ap; diff --git a/common/core/Logger.cxx b/common/core/Logger.cxx index bb494acc7b..e506417538 100644 --- a/common/core/Logger.cxx +++ b/common/core/Logger.cxx @@ -26,6 +26,7 @@ #include #include +#include #include #include #include diff --git a/common/core/Logger.h b/common/core/Logger.h index 5291915f80..e81787d559 100644 --- a/common/core/Logger.h +++ b/common/core/Logger.h @@ -24,6 +24,8 @@ #include #include +#include + // Each log writer instance has a unique textual name, // and is attached to a particular Logger instance and // is assigned a particular log level. @@ -46,7 +48,7 @@ namespace core { virtual void write(int level, const char *logname, const char *text) = 0; void write(int level, const char *logname, const char* format, va_list ap) - __attribute__((__format__ (__printf__, 4, 0))); + CORE_FORMAT_PRINTF(4, 0); // -=- Register a logger diff --git a/common/core/Logger_file.h b/common/core/Logger_file.h index 6bce3e2df5..e7080d20ef 100644 --- a/common/core/Logger_file.h +++ b/common/core/Logger_file.h @@ -25,6 +25,7 @@ #include #include +#include namespace core { diff --git a/common/core/Rect.h b/common/core/Rect.h index e4cb1634b7..ec9b142901 100644 --- a/common/core/Rect.h +++ b/common/core/Rect.h @@ -23,6 +23,8 @@ #include +#include + namespace core { // core::Point @@ -38,15 +40,15 @@ namespace core { Point() : x(0), y(0) {} Point(int x_, int y_) : x(x_), y(y_) {} inline Point negate() const - __attribute__ ((warn_unused_result)) + CORE_WARN_UNUSED_RESULT {return Point(-x, -y);} inline bool operator==(const Point &p) const {return x==p.x && y==p.y;} inline bool operator!=(const Point &p) const {return x!=p.x || y!=p.y;} inline Point translate(const Point &p) const - __attribute__ ((warn_unused_result)) + CORE_WARN_UNUSED_RESULT {return Point(x+p.x, y+p.y);} inline Point subtract(const Point &p) const - __attribute__ ((warn_unused_result)) + CORE_WARN_UNUSED_RESULT {return Point(x-p.x, y-p.y);} int x, y; }; @@ -70,7 +72,7 @@ namespace core { tl.x = x; tl.y = y; br.x = x+w; br.y = y+h; } inline Rect intersect(const Rect &r) const - __attribute__ ((warn_unused_result)) + CORE_WARN_UNUSED_RESULT { Rect result; result.tl.x = std::max(tl.x, r.tl.x); @@ -80,7 +82,7 @@ namespace core { return result; } inline Rect union_boundary(const Rect &r) const - __attribute__ ((warn_unused_result)) + CORE_WARN_UNUSED_RESULT { if (r.is_empty()) return *this; if (is_empty()) return r; @@ -92,7 +94,7 @@ namespace core { return result; } inline Rect translate(const Point &p) const - __attribute__ ((warn_unused_result)) + CORE_WARN_UNUSED_RESULT { return Rect(tl.translate(p), br.translate(p)); } diff --git a/common/core/Region.h b/common/core/Region.h index 729f14754a..87b2458e69 100644 --- a/common/core/Region.h +++ b/common/core/Region.h @@ -25,6 +25,7 @@ #include #include +#include struct pixman_region16; @@ -58,11 +59,11 @@ namespace core { // the following three operations return a new region: Region intersect(const Region& r) const - __attribute__ ((warn_unused_result)); + CORE_WARN_UNUSED_RESULT; Region union_(const Region& r) const - __attribute__ ((warn_unused_result)); + CORE_WARN_UNUSED_RESULT; Region subtract(const Region& r) const - __attribute__ ((warn_unused_result)); + CORE_WARN_UNUSED_RESULT; bool operator==(const Region& b) const; bool operator!=(const Region& b) const; diff --git a/common/core/Timer.cxx b/common/core/Timer.cxx index e50792cde6..698b39575c 100644 --- a/common/core/Timer.cxx +++ b/common/core/Timer.cxx @@ -24,12 +24,17 @@ #endif #include +#ifdef _WIN32 +#include +#else #include +#endif #include #include #include +#include #include using namespace core; diff --git a/common/core/Timer.h b/common/core/Timer.h index cde672b24c..10b0c18237 100644 --- a/common/core/Timer.h +++ b/common/core/Timer.h @@ -21,7 +21,11 @@ #define __CORE_TIMER_H__ #include +#ifdef _WIN32 +#include +#else #include +#endif namespace core { diff --git a/common/core/compiler.h b/common/core/compiler.h new file mode 100644 index 0000000000..5eded87a43 --- /dev/null +++ b/common/core/compiler.h @@ -0,0 +1,37 @@ +/* Copyright 2026 jose-pr + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __CORE_COMPILER_H__ +#define __CORE_COMPILER_H__ + +/* + * A handful of GCC/Clang-only attributes have no MSVC equivalent; these + * macros expand to the attribute where supported and to nothing otherwise. + */ +#if defined(__GNUC__) || defined(__clang__) +#define CORE_FORMAT_PRINTF(fmt_idx, args_idx) \ + __attribute__((__format__ (__printf__, fmt_idx, args_idx))) +#define CORE_FORMAT_ARG(idx) __attribute__ ((format_arg (idx))) +#define CORE_WARN_UNUSED_RESULT __attribute__ ((warn_unused_result)) +#else +#define CORE_FORMAT_PRINTF(fmt_idx, args_idx) +#define CORE_FORMAT_ARG(idx) +#define CORE_WARN_UNUSED_RESULT +#endif + +#endif // __CORE_COMPILER_H__ diff --git a/common/core/i18n.cxx b/common/core/i18n.cxx index a75b766739..4a17750894 100644 --- a/common/core/i18n.cxx +++ b/common/core/i18n.cxx @@ -32,6 +32,7 @@ #include #endif +#include #include // Restore original functions @@ -130,17 +131,38 @@ static void initTranslations() bind_textdomain_codeset("libc", "UTF-8"); } +/* + * When ENABLE_NLS is off, gettext.h's own dgettext/dcgettext/dngettext/ + * dcngettext fallbacks are plain macros (not real functions), and those + * macros were already permanently clobbered by core/i18n.h's earlier + * "#define dgettext dgettext_rfb"-style aliasing (used to redirect the + * rest of the codebase's calls here) -- a #undef can't restore a macro's + * prior definition, only remove the current one. So with NLS disabled + * there is nothing left upstream to call into; implement the same + * pass-through semantics gettext.h itself would have, directly. + */ + const char *dgettext_rfb(const char *domainname, const char *msgid) { initTranslations(); +#if defined ENABLE_NLS && ENABLE_NLS return dgettext(domainname, msgid); +#else + (void)domainname; + return msgid; +#endif } const char *dcgettext_rfb(const char *domainname, const char *msgid, int category) { initTranslations(); +#if defined ENABLE_NLS && ENABLE_NLS return dcgettext(domainname, msgid, category); +#else + (void)domainname; (void)category; + return msgid; +#endif } const char *dngettext_rfb(const char *domainname, const char *msgid, @@ -148,7 +170,12 @@ const char *dngettext_rfb(const char *domainname, const char *msgid, unsigned long int n) { initTranslations(); +#if defined ENABLE_NLS && ENABLE_NLS return dngettext(domainname, msgid, msgid_plural, n); +#else + (void)domainname; + return (n == 1) ? msgid : msgid_plural; +#endif } const char *dcngettext_rfb(const char *domainname, const char *msgid, @@ -156,7 +183,12 @@ const char *dcngettext_rfb(const char *domainname, const char *msgid, unsigned long int n, int category) { initTranslations(); +#if defined ENABLE_NLS && ENABLE_NLS return dcngettext(domainname, msgid, msgid_plural, n, category); +#else + (void)domainname; (void)category; + return (n == 1) ? msgid : msgid_plural; +#endif } const char *pgettext_rfb(const char *domain, diff --git a/common/core/i18n.h b/common/core/i18n.h index 83c05d9781..11a06bd67c 100644 --- a/common/core/i18n.h +++ b/common/core/i18n.h @@ -58,6 +58,8 @@ extern int swprintf (wchar_t *, size_t, const wchar_t *, ...) /* __attribute__((__format__ (__wprintf__, 3, 4))) */; #endif +#include + #define _(String) gettext (String) #define C_(Context, String) pgettext (Context, String) #define N_(String) gettext_noop (String) @@ -81,31 +83,31 @@ extern "C" { #endif const char *dgettext_rfb(const char *domainname, const char *msgid) - __attribute__ ((format_arg (2))); + CORE_FORMAT_ARG(2); const char *dcgettext_rfb(const char *domainname, const char *msgid, int category) - __attribute__ ((format_arg (2))); + CORE_FORMAT_ARG(2); const char *dngettext_rfb(const char *domainname, const char *msgid, const char *msgid_plural, unsigned long int n) - __attribute__ ((format_arg (2))) - __attribute__ ((format_arg (3))); + CORE_FORMAT_ARG(2) + CORE_FORMAT_ARG(3); const char *dcngettext_rfb(const char *domainname, const char *msgid, const char *msgid_plural, unsigned long int n, int category) - __attribute__ ((format_arg (2))) - __attribute__ ((format_arg (3))); + CORE_FORMAT_ARG(2) + CORE_FORMAT_ARG(3); const char *pgettext_rfb(const char *domain, const char *msg_ctxt_id, const char *msgid, int category) - __attribute__ ((format_arg (3))); + CORE_FORMAT_ARG(3); const char *npgettext_rfb(const char *domain, const char *msg_ctxt_id, const char *msgid, const char *msgid_plural, unsigned long int n, int category) - __attribute__ ((format_arg (3))) - __attribute__ ((format_arg (4))); + CORE_FORMAT_ARG(3) + CORE_FORMAT_ARG(4); #ifdef __cplusplus } diff --git a/common/core/os.h b/common/core/os.h new file mode 100644 index 0000000000..6177acffe9 --- /dev/null +++ b/common/core/os.h @@ -0,0 +1,134 @@ +/* Copyright 2026 jose-pr + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __CORE_OS_H__ +#define __CORE_OS_H__ + +/* + * MSVC lacks a number of POSIX APIs that this project's previous Windows + * toolchain (mingw, a GCC/glibc-compatible environment) provided + * transparently. Centralize the portable equivalents/shims here rather + * than scattering per-file fixes. + */ +#ifdef _WIN32 + +#include // _MAX_PATH +#include // _stricmp, _strnicmp +#include // struct timeval, FILETIME + +#ifndef PATH_MAX +#define PATH_MAX _MAX_PATH +#endif + +/* mingw is a GCC/glibc-compatible environment that provides all of the + * below for real, just from the POSIX headers rather than the ones a + * Windows source file would otherwise include. Pull those in here so + * callers get the genuine declarations, and shim only for MSVC -- both + * halves matter, as redefining them under mingw collides with the real + * ones. */ +#ifndef _MSC_VER + +#include // strcasecmp, strncasecmp +#include // gettimeofday +#include // mode_t +#include // dirname +#include // usleep + +#else + +#define strcasecmp _stricmp +#define strncasecmp _strnicmp + +typedef int mode_t; + +/* POSIX ssize_t: a signed size. MSVC only spells it SSIZE_T, from + * , which above already pulls in. */ +typedef SSIZE_T ssize_t; + +inline int gettimeofday(struct timeval *tv, void *) +{ + /* FILETIME is in 100ns intervals since 1601-01-01; convert to + * microseconds since the Unix epoch (1970-01-01). */ + static const unsigned long long kEpochDiff = 116444736000000000ULL; + FILETIME ft; + unsigned long long t; + + GetSystemTimeAsFileTime(&ft); + t = ((unsigned long long)ft.dwHighDateTime << 32) | ft.dwLowDateTime; + t = (t - kEpochDiff) / 10; + + tv->tv_sec = (long)(t / 1000000ULL); + tv->tv_usec = (long)(t % 1000000ULL); + + return 0; +} + +/* POSIX ffs(): one plus the index of the least significant set bit, or + * zero if there is none. mingw supplies it from ; MSVC has no + * equivalent, so map it onto the intrinsic. */ +#include + +inline int ffs(int i) +{ + unsigned long bit; + + if (!_BitScanForward(&bit, (unsigned long)i)) + return 0; + + return (int)bit + 1; +} + +/* POSIX usleep(). Windows only offers millisecond granularity, so a + * sub-millisecond request rounds up to 1ms rather than to 0 -- sleeping + * for less than asked is the one behaviour a caller cannot correct for. */ +inline int usleep(unsigned long usec) +{ + Sleep((DWORD)((usec + 999) / 1000)); + return 0; +} + +/* POSIX dirname(), minimally: strip the trailing filename component, + * accepting both path separators since Windows paths use either. Modifies + * and returns its argument, matching the (permitted) in-place POSIX form. */ +inline char *dirname(char *path) +{ + char *slash = strrchr(path, '\\'); + char *fwdslash = strrchr(path, '/'); + + if (fwdslash > slash) + slash = fwdslash; + + if (slash == nullptr) { + static char dot[] = "."; + return dot; + } + + if (slash == path) { + *(slash + 1) = '\0'; + return path; + } + + *slash = '\0'; + return path; +} + +#endif // _MSC_VER + +#endif // _WIN32 + +#endif // __CORE_OS_H__ diff --git a/common/core/string.h b/common/core/string.h index a67ade3b5a..af5beb2c5d 100644 --- a/common/core/string.h +++ b/common/core/string.h @@ -29,11 +29,13 @@ #include #include +#include + namespace core { // Formats according to printf(), with a dynamic allocation std::string format(const char *fmt, ...) - __attribute__((__format__ (__printf__, 1, 2))); + CORE_FORMAT_PRINTF(1, 2); // Splits a string with the specified delimiter std::vector split(const char* src, diff --git a/common/core/time.cxx b/common/core/time.cxx index 47a0ff8ad6..ffdedb73c6 100644 --- a/common/core/time.cxx +++ b/common/core/time.cxx @@ -22,8 +22,13 @@ #endif #include +#ifdef _WIN32 +#include +#else #include +#endif +#include #include namespace core { diff --git a/common/core/xdgdirs.cxx b/common/core/xdgdirs.cxx index 2628f317b5..e6c748c5ba 100644 --- a/common/core/xdgdirs.cxx +++ b/common/core/xdgdirs.cxx @@ -37,10 +37,13 @@ #include #include /* MinGW needs it */ #include +#include /* _mkdir */ #define stat _stat -#define mkdir(path, mode) mkdir(path) +/* MSVC only provides _mkdir (no bare mkdir symbol at all, unlike mingw) */ +#define mkdir(path, mode) _mkdir(path) #endif +#include #include static const char* getvncdir(bool userDir, const char *xdg_env, const char *xdg_def) diff --git a/common/core/xdgdirs.h b/common/core/xdgdirs.h index 0769ba8bae..7f642f1ec4 100644 --- a/common/core/xdgdirs.h +++ b/common/core/xdgdirs.h @@ -22,6 +22,8 @@ #include +#include + namespace core { /* diff --git a/common/network/TcpSocket.cxx b/common/network/TcpSocket.cxx index 3d6f22858a..dca9cd23e4 100644 --- a/common/network/TcpSocket.cxx +++ b/common/network/TcpSocket.cxx @@ -39,7 +39,9 @@ #include #include #include +#ifndef WIN32 #include +#endif #include #include diff --git a/common/rdr/BufferedInStream.cxx b/common/rdr/BufferedInStream.cxx index 0ebbd65508..885dae4672 100644 --- a/common/rdr/BufferedInStream.cxx +++ b/common/rdr/BufferedInStream.cxx @@ -23,6 +23,7 @@ #include +#include #include #include diff --git a/common/rdr/BufferedInStream.h b/common/rdr/BufferedInStream.h index b3d6115e08..2d4a961c69 100644 --- a/common/rdr/BufferedInStream.h +++ b/common/rdr/BufferedInStream.h @@ -24,7 +24,11 @@ #ifndef __RDR_BUFFEREDINSTREAM_H__ #define __RDR_BUFFEREDINSTREAM_H__ +#ifdef _WIN32 +#include +#else #include +#endif #include diff --git a/common/rdr/BufferedOutStream.cxx b/common/rdr/BufferedOutStream.cxx index ee423eba3b..b2372b871c 100644 --- a/common/rdr/BufferedOutStream.cxx +++ b/common/rdr/BufferedOutStream.cxx @@ -22,6 +22,7 @@ #include #endif +#include #include #include diff --git a/common/rdr/BufferedOutStream.h b/common/rdr/BufferedOutStream.h index dd765dc9ed..ed18cf84e0 100644 --- a/common/rdr/BufferedOutStream.h +++ b/common/rdr/BufferedOutStream.h @@ -24,7 +24,11 @@ #ifndef __RDR_BUFFEREDOUTSTREAM_H__ #define __RDR_BUFFEREDOUTSTREAM_H__ +#ifdef _WIN32 +#include +#else #include +#endif #include diff --git a/common/rdr/FdInStream.cxx b/common/rdr/FdInStream.cxx index 25542a0149..a4be35659c 100644 --- a/common/rdr/FdInStream.cxx +++ b/common/rdr/FdInStream.cxx @@ -23,7 +23,6 @@ #include #include #include -#include #ifdef _WIN32 #include #define errorNumber WSAGetLastError() @@ -32,6 +31,7 @@ #else #include #include +#include #include #define errorNumber errno #endif diff --git a/common/rdr/FdOutStream.cxx b/common/rdr/FdOutStream.cxx index 416926c1ec..61efb3dd4e 100644 --- a/common/rdr/FdOutStream.cxx +++ b/common/rdr/FdOutStream.cxx @@ -44,6 +44,7 @@ #include #endif +#include #include #include diff --git a/common/rdr/FdOutStream.h b/common/rdr/FdOutStream.h index d9f16efb71..f37fe97f7c 100644 --- a/common/rdr/FdOutStream.h +++ b/common/rdr/FdOutStream.h @@ -24,7 +24,11 @@ #ifndef __RDR_FDOUTSTREAM_H__ #define __RDR_FDOUTSTREAM_H__ +#ifdef _WIN32 +#include +#else #include +#endif #include diff --git a/common/rfb/Congestion.cxx b/common/rfb/Congestion.cxx index 46bae00da4..278263220c 100644 --- a/common/rfb/Congestion.cxx +++ b/common/rfb/Congestion.cxx @@ -39,7 +39,11 @@ #include #include +#ifdef _WIN32 +#include +#else #include +#endif #ifdef __linux__ #include @@ -50,6 +54,7 @@ #endif #include +#include #include #include diff --git a/common/rfb/H264WinDecoderContext.cxx b/common/rfb/H264WinDecoderContext.cxx index 1bc7968d5c..99a80f54c0 100644 --- a/common/rfb/H264WinDecoderContext.cxx +++ b/common/rfb/H264WinDecoderContext.cxx @@ -36,10 +36,18 @@ using namespace rfb; -// Older MinGW lacks this definition -#ifndef HAVE_VIDEO_PROCESSOR_MFT -static GUID CLSID_VideoProcessorMFT = { 0x88753b26, 0x5b24, 0x49bd, { 0xb2, 0xe7, 0xc, 0x44, 0x5c, 0x78, 0xc9, 0x82 } }; -#endif +/* + * CLSID_VideoProcessorMFT's GUID data ships in wmcodecdspuuid.lib + * regardless of target, but recent Windows SDKs (mfidl.h) only *declare* + * it when building against WINVER < _WIN32_WINNT_WINTHRESHOLD (i.e. pre- + * Windows-10); above that the declaration is removed from the header even + * though the linkable symbol still exists. Older MinGW headers never + * declared it either. Use our own name unconditionally instead of trying + * to detect header visibility (CMake's check_variable_exists only tests + * linkability, not header visibility, so it can't tell the two cases + * apart -- see CMakeLists.txt history). + */ +static const GUID kCLSID_VideoProcessorMFT = { 0x88753b26, 0x5b24, 0x49bd, { 0xb2, 0xe7, 0xc, 0x44, 0x5c, 0x78, 0xc9, 0x82 } }; H264WinDecoderContext::H264WinDecoderContext(const core::Rect &r) : H264DecoderContext(r) @@ -50,7 +58,7 @@ H264WinDecoderContext::H264WinDecoderContext(const core::Rect &r) if (FAILED(CoCreateInstance(CLSID_CMSH264DecoderMFT, nullptr, CLSCTX_INPROC_SERVER, IID_IMFTransform, (LPVOID*)&decoder))) throw std::runtime_error(_("Could not find video codec")); - if (FAILED(CoCreateInstance(CLSID_VideoProcessorMFT, nullptr, CLSCTX_INPROC_SERVER, IID_IMFTransform, (LPVOID*)&converter))) + if (FAILED(CoCreateInstance(kCLSID_VideoProcessorMFT, nullptr, CLSCTX_INPROC_SERVER, IID_IMFTransform, (LPVOID*)&converter))) { if (FAILED(CoCreateInstance(CLSID_CColorConvertDMO, nullptr, CLSCTX_INPROC_SERVER, IID_IMFTransform, (LPVOID*)&converter))) { diff --git a/common/rfb/SSecurityRSAAES.cxx b/common/rfb/SSecurityRSAAES.cxx index 398eff4de1..d08d670aa0 100644 --- a/common/rfb/SSecurityRSAAES.cxx +++ b/common/rfb/SSecurityRSAAES.cxx @@ -38,6 +38,7 @@ #include #include +#include #include #include #include diff --git a/common/rfb/Security.cxx b/common/rfb/Security.cxx index 58eea010b3..7841d835f7 100644 --- a/common/rfb/Security.cxx +++ b/common/rfb/Security.cxx @@ -27,6 +27,7 @@ #include #include +#include #include #include diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h index 07392794fe..1f79d80b26 100644 --- a/common/rfb/VNCServerST.h +++ b/common/rfb/VNCServerST.h @@ -24,7 +24,11 @@ #ifndef __RFB_VNCSERVERST_H__ #define __RFB_VNCSERVERST_H__ +#ifdef _WIN32 +#include +#else #include +#endif #include diff --git a/common/rfb/encodings.cxx b/common/rfb/encodings.cxx index 21bd874c0d..fb03229960 100644 --- a/common/rfb/encodings.cxx +++ b/common/rfb/encodings.cxx @@ -22,6 +22,7 @@ #include +#include #include #include diff --git a/tests/perf/decperf.cxx b/tests/perf/decperf.cxx index 1ac3a535a1..6976b43fc8 100644 --- a/tests/perf/decperf.cxx +++ b/tests/perf/decperf.cxx @@ -31,7 +31,11 @@ #include #include #include +#ifdef _WIN32 +#include +#else #include +#endif #include #include @@ -42,6 +46,7 @@ #include #include +#include #include "util.h" // FIXME: Files are always in this format diff --git a/tests/perf/encperf.cxx b/tests/perf/encperf.cxx index 7eb3898407..da3de44131 100644 --- a/tests/perf/encperf.cxx +++ b/tests/perf/encperf.cxx @@ -35,7 +35,11 @@ #include #include #include +#ifdef _WIN32 +#include +#else #include +#endif #include @@ -52,6 +56,7 @@ #include #include +#include #include "util.h" static core::IntParameter width("width", "Frame buffer width", 0); diff --git a/tests/perf/fbperf.cxx b/tests/perf/fbperf.cxx index 1a5b5ba713..512492c4be 100644 --- a/tests/perf/fbperf.cxx +++ b/tests/perf/fbperf.cxx @@ -21,7 +21,11 @@ #endif #include +#ifdef _WIN32 +#include +#else #include +#endif #include #include @@ -33,6 +37,7 @@ #include "../vncviewer/PlatformPixelBuffer.h" +#include #include "util.h" class TestWindow: public Fl_Window { diff --git a/tests/unit/emulatemb.cxx b/tests/unit/emulatemb.cxx index d59ce509d3..3f0c80e8e3 100644 --- a/tests/unit/emulatemb.cxx +++ b/tests/unit/emulatemb.cxx @@ -21,7 +21,11 @@ #include #endif +#ifdef _WIN32 +#include +#else #include +#endif #include diff --git a/tests/unit/gesturehandler.cxx b/tests/unit/gesturehandler.cxx index cf2e5b6bd4..1d0c284c01 100644 --- a/tests/unit/gesturehandler.cxx +++ b/tests/unit/gesturehandler.cxx @@ -20,7 +20,11 @@ #include #endif +#ifdef _WIN32 +#include +#else #include +#endif #include diff --git a/vncviewer/BaseTouchHandler.cxx b/vncviewer/BaseTouchHandler.cxx index 3100f86b98..f49d09bd19 100644 --- a/vncviewer/BaseTouchHandler.cxx +++ b/vncviewer/BaseTouchHandler.cxx @@ -30,6 +30,7 @@ #include #include "GestureHandler.h" +#include #include "BaseTouchHandler.h" // Sensitivity threshold for gestures diff --git a/vncviewer/BaseTouchHandler.h b/vncviewer/BaseTouchHandler.h index 8db658c0fd..9a30446d0e 100644 --- a/vncviewer/BaseTouchHandler.h +++ b/vncviewer/BaseTouchHandler.h @@ -22,7 +22,11 @@ #include "GestureEvent.h" +#ifdef _WIN32 +#include +#else #include +#endif class BaseTouchHandler { public: diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx index 199b2e8118..55b0f6fec3 100644 --- a/vncviewer/DesktopWindow.cxx +++ b/vncviewer/DesktopWindow.cxx @@ -23,11 +23,22 @@ #include +/* + * MSVC only recognizes the and/or/not alternative operator tokens via this + * standard header; GCC/Clang support them natively and treat this as a + * no-op, so it's safe to include unconditionally. + */ +#include + #include #include #include #include +#ifdef _WIN32 +#include +#else #include +#endif #include #include @@ -37,6 +48,7 @@ #include #include +#include #include "DesktopWindow.h" #include "OptionsDialog.h" #include "parameters.h" diff --git a/vncviewer/DesktopWindow.h b/vncviewer/DesktopWindow.h index af1b7474df..5eb936313f 100644 --- a/vncviewer/DesktopWindow.h +++ b/vncviewer/DesktopWindow.h @@ -24,7 +24,13 @@ #include #include +#ifdef _WIN32 +#include +#else #include +#endif + +#include #include @@ -86,9 +92,9 @@ class DesktopWindow : public Fl_Window { private: void addOverlayTip(const char *text, ...) - __attribute__((__format__ (__printf__, 2, 3))); + CORE_FORMAT_PRINTF(2, 3); void addOverlayError(const char *text, ...) - __attribute__((__format__ (__printf__, 2, 3))); + CORE_FORMAT_PRINTF(2, 3); void addOverlay(const char *text); static void updateOverlay(void *data); diff --git a/vncviewer/GestureHandler.cxx b/vncviewer/GestureHandler.cxx index 2ede991b22..4b435c62b3 100644 --- a/vncviewer/GestureHandler.cxx +++ b/vncviewer/GestureHandler.cxx @@ -18,7 +18,13 @@ */ #ifdef HAVE_CONFIG_H - #include + /* MSVC only defines M_PI and friends when asked, and this must come + * before any math header. gettimeofday() comes from core/os.h. */ +#ifdef _WIN32 +#define _USE_MATH_DEFINES +#include +#endif +#include #endif #include diff --git a/vncviewer/ServerDialog.cxx b/vncviewer/ServerDialog.cxx index 2dc2824b5f..dc34426d9a 100644 --- a/vncviewer/ServerDialog.cxx +++ b/vncviewer/ServerDialog.cxx @@ -23,7 +23,11 @@ #include #include +#ifndef WIN32 #include +#endif + +#include // FIXME: Workaround for FLTK including windows.h #ifdef WIN32 diff --git a/vncviewer/ShortcutHandler.cxx b/vncviewer/ShortcutHandler.cxx index 05ba75fb6a..af7cfa02e0 100644 --- a/vncviewer/ShortcutHandler.cxx +++ b/vncviewer/ShortcutHandler.cxx @@ -25,6 +25,7 @@ #define XK_MISCELLANY #include +#include #include "ShortcutHandler.h" ShortcutHandler::ShortcutHandler() : diff --git a/vncviewer/vncviewer.cxx b/vncviewer/vncviewer.cxx index 20a0174b8c..410c0f9e62 100644 --- a/vncviewer/vncviewer.cxx +++ b/vncviewer/vncviewer.cxx @@ -30,12 +30,14 @@ #include #include #include -#include #include #ifdef WIN32 #include #include +#include // _open_osfhandle +#else +#include #endif #include @@ -378,20 +380,33 @@ static void usage(const char *programName) } #endif + /* + * MSVC rejects preprocessor directives inside a macro argument list + * (the _() call below); GCC/Clang tolerate it as an extension, but it's + * undefined behavior per the standard. Duplicate the two variants + * in full instead of straddling the macro call with #ifndef/#endif. + */ +#ifndef WIN32 fprintf(stderr, _( "\n" "Usage: %s [parameters] [host][:displayNum]\n" " %s [parameters] [host][::port]\n" -#ifndef WIN32 " %s [parameters] [unix socket]\n" -#endif " %s [parameters] -listen [port]\n" " %s [parameters] [.tigervnc file]\n"), programName, programName, -#ifndef WIN32 programName, -#endif programName, programName); +#else + fprintf(stderr, _( + "\n" + "Usage: %s [parameters] [host][:displayNum]\n" + " %s [parameters] [host][::port]\n" + " %s [parameters] -listen [port]\n" + " %s [parameters] [.tigervnc file]\n"), + programName, programName, + programName, programName); +#endif #if !defined(WIN32) && !defined(__APPLE__) fprintf(stderr, _("\n" diff --git a/vncviewer/vncviewer.h b/vncviewer/vncviewer.h index 44c5f53e97..74e5c75130 100644 --- a/vncviewer/vncviewer.h +++ b/vncviewer/vncviewer.h @@ -19,12 +19,14 @@ #ifndef __VNCVIEWER_H__ #define __VNCVIEWER_H__ +#include + #define VNCSERVERNAMELEN 256 void abort_vncviewer(const char *error, ...) - __attribute__((__format__ (__printf__, 1, 2))); + CORE_FORMAT_PRINTF(1, 2); void abort_connection(const char *error, ...) - __attribute__((__format__ (__printf__, 1, 2))); + CORE_FORMAT_PRINTF(1, 2); void abort_connection_with_unexpected_error(const std::exception &); void disconnect(); diff --git a/win/rfb_win32/DeviceFrameBuffer.cxx b/win/rfb_win32/DeviceFrameBuffer.cxx index d20b6dc764..7bc08d77cd 100644 --- a/win/rfb_win32/DeviceFrameBuffer.cxx +++ b/win/rfb_win32/DeviceFrameBuffer.cxx @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -185,9 +186,9 @@ void DeviceFrameBuffer::setCursor(HCURSOR hCursor, VNCServer* server) // We may not get the RGBA order we want, so shuffle things around int ridx, gidx, bidx, aidx; - ridx = __builtin_ffs(bi.bV5RedMask) / 8; - gidx = __builtin_ffs(bi.bV5GreenMask) / 8; - bidx = __builtin_ffs(bi.bV5BlueMask) / 8; + ridx = ffs(bi.bV5RedMask) / 8; + gidx = ffs(bi.bV5GreenMask) / 8; + bidx = ffs(bi.bV5BlueMask) / 8; // Usually not set properly aidx = 6 - ridx - gidx - bidx; diff --git a/win/vncconfig/Authentication.h b/win/vncconfig/Authentication.h index 3ee7b049bc..79263e2956 100644 --- a/win/vncconfig/Authentication.h +++ b/win/vncconfig/Authentication.h @@ -21,6 +21,10 @@ // FIXME: The implementation should be moved to Authentication.cxx so we // don't have all of these #includes polluting everything +/* WIN32_LEAN_AND_MEAN keeps from pulling this in, and the + * common-dialog types below need it. mingw's headers included it + * transitively, which is why this was never needed before. */ +#include #include #include diff --git a/win/winvnc/VNCServerService.cxx b/win/winvnc/VNCServerService.cxx index 4da0a5dc08..9a8ac50745 100644 --- a/win/winvnc/VNCServerService.cxx +++ b/win/winvnc/VNCServerService.cxx @@ -43,7 +43,9 @@ const char* winvnc::VNCServerService::Name = "TigerVNC"; // SendSAS is not available until Windows 7, and missing from MinGW static HMODULE sasLibrary = nullptr; -typedef void WINAPI (*SendSAS_proto)(BOOL AsUser); +/* The calling convention belongs on the pointer, not before it. gcc + * accepts the transposed form; MSVC rejects it outright. */ +typedef void (WINAPI *SendSAS_proto)(BOOL AsUser); static SendSAS_proto _SendSAS = nullptr; VNCServerService::VNCServerService() diff --git a/win/wm_hooks/wm_hooks.cxx b/win/wm_hooks/wm_hooks.cxx index 2f04b85173..4e95d69bcf 100644 --- a/win/wm_hooks/wm_hooks.cxx +++ b/win/wm_hooks/wm_hooks.cxx @@ -26,7 +26,22 @@ #include +/* These live in a shared data section so every process the hook DLL is + * injected into sees one copy. gcc marks each variable with an attribute; + * MSVC has no per-variable equivalent, so the groups below are bracketed + * by data_seg pragmas instead and SHARED itself expands to nothing. The + * linker directive is what actually makes the segment shared -- without + * it the section exists but every process gets its own copy. */ +#ifdef _MSC_VER +#pragma comment(linker, "/SECTION:shared,RWS") +#define SHARED +#define SHARED_BEGIN __pragma(data_seg("shared")) +#define SHARED_END __pragma(data_seg()) +#else #define SHARED __attribute__((section ("shared"), shared)) +#define SHARED_BEGIN +#define SHARED_END +#endif UINT WM_HK_PingThread = RegisterWindowMessage("RFB.WM_Hooks.PingThread"); @@ -89,6 +104,7 @@ BOOL WINAPI DllMain(HANDLE instance, ULONG reason, LPVOID /*reserved*/) { // -=- Display update hooks // +SHARED_BEGIN DWORD hook_owner SHARED = 0; DWORD hook_target SHARED = 0; HHOOK hook_CallWndProc SHARED = nullptr; @@ -100,6 +116,7 @@ HCURSOR cursor SHARED = nullptr; #ifdef _DEBUG UINT diagnostic_min SHARED =1; UINT diagnostic_max SHARED =0; +SHARED_END #endif #ifdef _DEBUG @@ -375,12 +392,14 @@ BOOL WM_Hooks_Remove(DWORD owner) { // -=- User input hooks // +SHARED_BEGIN HHOOK hook_keyboard SHARED = nullptr; HHOOK hook_pointer SHARED = nullptr; bool enable_real_ptr SHARED = true; bool enable_synth_ptr SHARED = true; bool enable_real_kbd SHARED = true; bool enable_synth_kbd SHARED = true; +SHARED_END #ifdef WH_KEYBOARD_LL LRESULT CALLBACK HookKeyboardHook(int nCode, WPARAM wParam, LPARAM lParam) {