nana/source/detail/platform_abstraction.cpp

408 lines
10 KiB
C++

#include "platform_abstraction.hpp"
#include <nana/deploy.hpp>
#include "../paint/truetype.hpp"
#ifdef NANA_WINDOWS
# include <windows.h>
///////////////////////////////////////////////////////////////////////////////////////////////////////
/******************************************************************
* *
* VersionHelpers.h -- This module defines helper functions to *
* promote version check with proper *
* comparisons. *
* *
* Copyright (c) Microsoft Corp. All rights reserved. *
* *
******************************************************************/
#include <specstrings.h> // for _In_, etc.
#if !defined(__midl) && !defined(SORTPP_PASS)
#if (NTDDI_VERSION >= NTDDI_WINXP)
#ifdef __cplusplus
#define VERSIONHELPERAPI inline bool
#else // __cplusplus
#define VERSIONHELPERAPI FORCEINLINE BOOL
#endif // __cplusplus
VERSIONHELPERAPI
IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor)
{
OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, { 0 }, 0, 0 };
DWORDLONG const dwlConditionMask = VerSetConditionMask(
VerSetConditionMask(
VerSetConditionMask(
0, VER_MAJORVERSION, VER_GREATER_EQUAL),
VER_MINORVERSION, VER_GREATER_EQUAL),
VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
osvi.dwMajorVersion = wMajorVersion;
osvi.dwMinorVersion = wMinorVersion;
osvi.wServicePackMajor = wServicePackMajor;
return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
}
VERSIONHELPERAPI
IsWindowsXPOrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 0);
}
VERSIONHELPERAPI
IsWindowsXPSP1OrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 1);
}
VERSIONHELPERAPI
IsWindowsXPSP2OrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 2);
}
VERSIONHELPERAPI
IsWindowsXPSP3OrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 3);
}
VERSIONHELPERAPI
IsWindowsVistaOrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 0);
}
VERSIONHELPERAPI
IsWindowsVistaSP1OrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 1);
}
VERSIONHELPERAPI
IsWindowsVistaSP2OrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 2);
}
VERSIONHELPERAPI
IsWindows7OrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 0);
}
VERSIONHELPERAPI
IsWindows7SP1OrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 1);
}
#ifndef _WIN32_WINNT_WIN8 // (0x0602)
#define _WIN32_WINNT_WIN8 (0x0602)
#endif // _WIN32_WINNT_WIN8(0x0602)
VERSIONHELPERAPI
IsWindows8OrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0);
}
#ifndef _WIN32_WINNT_WINBLUE // (0x0602)
#define _WIN32_WINNT_WINBLUE (0x0602)
#endif // _WIN32_WINNT_WINBLUE (0x0602)
VERSIONHELPERAPI
IsWindows8Point1OrGreater()
{
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINBLUE), LOBYTE(_WIN32_WINNT_WINBLUE), 0);
}
VERSIONHELPERAPI
IsWindowsServer()
{
OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, { 0 }, 0, 0, 0, VER_NT_WORKSTATION };
DWORDLONG const dwlConditionMask = VerSetConditionMask(0, VER_PRODUCT_TYPE, VER_EQUAL);
return !VerifyVersionInfoW(&osvi, VER_PRODUCT_TYPE, dwlConditionMask);
}
#endif // NTDDI_VERSION
#endif // defined(__midl)
#else
# include "posix/platform_spec.hpp"
# include <fontconfig/fontconfig.h>
# if defined(NANA_USE_XFT)
# include <X11/Xft/Xft.h>
# include <iconv.h>
# include <fstream>
# endif
#endif
namespace nana
{
class internal_font
: public font_interface
{
public:
using path_type = std::experimental::filesystem::path;
internal_font(const path_type& ttf, const std::string& font_family, double font_size, const font_style& fs, native_font_type native_font):
ttf_(ttf),
family_(font_family),
size_(font_size),
style_(fs),
native_handle_(native_font)
{}
~internal_font()
{
#ifdef NANA_WINDOWS
::DeleteObject(reinterpret_cast<HFONT>(native_handle_));
#elif defined(NANA_X11)
auto disp = ::nana::detail::platform_spec::instance().open_display();
# ifdef NANA_USE_XFT
::XftFontClose(disp, reinterpret_cast<XftFont*>(native_handle_));
# else
::XFreeFontSet(disp, reinterpret_cast<XFontSet>(native_handle_));
# endif
#endif
if (!ttf_.empty())
platform_abstraction::font_resource(false, ttf_);
}
public:
const std::string& family() const override
{
return family_;
}
double size() const override
{
return size_;
}
const font_style & style() const override
{
return style_;
}
native_font_type native_handle() const override
{
return native_handle_;
}
private:
path_type const ttf_;
std::string const family_;
double const size_;
font_style const style_;
native_font_type const native_handle_;
};
struct platform_runtime
{
std::shared_ptr<font_interface> font;
#ifdef NANA_X11
std::map<std::string, std::size_t> fontconfig_counts;
#endif
};
namespace
{
namespace data
{
static platform_runtime* storage;
}
}
static platform_runtime& platform_storage()
{
if (nullptr == data::storage)
throw std::runtime_error("platform_abstraction is empty");
return *data::storage;
}
void platform_abstraction::initialize()
{
if (nullptr == data::storage)
data::storage = new platform_runtime;
}
void platform_abstraction::shutdown()
{
auto & r = platform_storage();
if (r.font.use_count() > 1)
throw std::runtime_error("platform_abstraction is disallowed to shutdown");
r.font.reset();
delete data::storage;
data::storage = nullptr;
}
::std::shared_ptr<platform_abstraction::font> platform_abstraction::default_font(const ::std::shared_ptr<font>& new_font)
{
auto & r = platform_storage();
if (new_font)
{
auto f = r.font;
if (new_font != r.font)
r.font = new_font;
return f;
}
if (!r.font)
r.font = make_font({}, 0, {});
return r.font;
}
static std::shared_ptr<platform_abstraction::font> font_factory(::std::string font_family, double size_pt, const platform_abstraction::font::font_style& fs, internal_font::path_type ttf)
{
using native_font_type = platform_abstraction::font::native_font_type;
#ifdef NANA_WINDOWS
std::wstring wfont_family = to_nstring(font_family);
//Make sure the length of font family less than LF_FACESIZE which is defined by Windows
if (wfont_family.length() + 1 > LF_FACESIZE)
wfont_family.clear();
//Translate pt to px
auto hDC = ::GetDC(nullptr);
auto font_height = -static_cast<LONG>(size_pt * ::GetDeviceCaps(hDC, LOGPIXELSY) / 72);
::ReleaseDC(nullptr, hDC);
if (wfont_family.empty() || (0 == font_height))
{
//Create default font object.
NONCLIENTMETRICS metrics = {};
metrics.cbSize = sizeof metrics;
#if(WINVER >= 0x0600)
#if defined(NANA_MINGW)
OSVERSIONINFO osvi = {};
osvi.dwOSVersionInfoSize = sizeof(osvi);
::GetVersionEx(&osvi);
if (osvi.dwMajorVersion < 6)
metrics.cbSize -= sizeof(metrics.iPaddedBorderWidth);
#else
if (!IsWindowsVistaOrGreater())
metrics.cbSize -= sizeof(metrics.iPaddedBorderWidth);
#endif
#endif
::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof metrics, &metrics, 0);
if (wfont_family.empty())
{
wfont_family = metrics.lfMessageFont.lfFaceName;
font_family = to_utf8(wfont_family);
}
if (0 == font_height)
font_height = metrics.lfMessageFont.lfHeight;
}
::LOGFONT lf{};
std::wcscpy(lf.lfFaceName, wfont_family.c_str());
lf.lfHeight = font_height;
lf.lfCharSet = DEFAULT_CHARSET;
lf.lfWeight = fs.weight;
lf.lfQuality = PROOF_QUALITY;
lf.lfPitchAndFamily = FIXED_PITCH;
lf.lfItalic = fs.italic;
lf.lfUnderline = fs.underline;
lf.lfStrikeOut = fs.strike_out;
auto fd = ::CreateFontIndirect(&lf);
#elif defined(NANA_X11)
auto disp = ::nana::detail::platform_spec::instance().open_display();
# ifdef NANA_USE_XFT
if(font_family.empty())
font_family = '*';
std::string pat_str = font_family + '-' + std::to_string(size_pt ? size_pt : 10);
auto pat = ::XftNameParse(pat_str.c_str());
XftResult res;
auto match_pat = ::XftFontMatch(disp, ::XDefaultScreen(disp), pat, &res);
::XftFont* fd = nullptr;
if (match_pat)
fd = ::XftFontOpenPattern(disp, match_pat);
# else
std::string pat_str;
if (font_family.empty())
pat_str = "-misc-fixed-*";
else
pat_str = "-misc-fixed-" + font_family;
char ** missing_list;
int missing_count;
char * defstr;
XFontSet fd = ::XCreateFontSet(display_, const_cast<char*>(pat_str.c_str()), &missing_list, &missing_count, &defstr);
# endif
#endif
if (fd)
return std::make_shared<internal_font>(std::move(ttf), std::move(font_family), size_pt, fs, reinterpret_cast<native_font_type>(fd));
return{};
}
::std::shared_ptr<platform_abstraction::font> platform_abstraction::make_font(const std::string& font_family, double size_pt, const font::font_style& fs)
{
return font_factory(font_family, size_pt, fs, {});
}
::std::shared_ptr<platform_abstraction::font> platform_abstraction::make_font_from_ttf(const path_type& ttf, double size_pt, const font::font_style& fs)
{
::nana::spec::truetype truetype{ ttf };
if (truetype.font_family().empty())
return nullptr;
font_resource(true, ttf);
return font_factory(truetype.font_family(), size_pt, fs, ttf);
}
void platform_abstraction::font_resource(bool try_add, const path_type& ttf)
{
#ifdef NANA_WINDOWS
if (try_add)
::AddFontResourceEx(ttf.wstring().c_str(), FR_PRIVATE, nullptr);
else
::RemoveFontResourceEx(ttf.wstring().c_str(), FR_PRIVATE, nullptr);
#else
auto & fc = platform_storage().fontconfig_counts;
if(try_add)
{
if(1 == ++(fc[ttf.string()]))
{
::FcConfigAppFontAddFile(nullptr, reinterpret_cast<const FcChar8*>(ttf.string().c_str()));
}
}
else
{
auto i = fc.find(ttf.string());
if(i != fc.end())
{
if(0 == --(i->second))
fc.erase(i);
if(0 == fc.size())
::FcConfigAppFontClear(nullptr);
}
}
#endif
}
}