Move all function pointers to the core data struct.

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2020-10-15 22:00:13 +08:00
parent 904bbb8576
commit e1656a9636
2 changed files with 274 additions and 274 deletions

View File

@ -137,8 +137,8 @@ Q_DECLARE_METATYPE(QMargins)
#define WNEF_EXECUTE_WINAPI(funcName, ...) funcName(__VA_ARGS__); #define WNEF_EXECUTE_WINAPI(funcName, ...) funcName(__VA_ARGS__);
#else #else
#define WNEF_EXECUTE_WINAPI(funcName, ...) \ #define WNEF_EXECUTE_WINAPI(funcName, ...) \
if (m_lp##funcName) { \ if (coreData()->m_lp##funcName) { \
m_lp##funcName(__VA_ARGS__); \ coreData()->m_lp##funcName(__VA_ARGS__); \
} }
#endif #endif
#endif #endif
@ -148,7 +148,7 @@ Q_DECLARE_METATYPE(QMargins)
#define WNEF_EXECUTE_WINAPI_RETURN(funcName, defVal, ...) funcName(__VA_ARGS__) #define WNEF_EXECUTE_WINAPI_RETURN(funcName, defVal, ...) funcName(__VA_ARGS__)
#else #else
#define WNEF_EXECUTE_WINAPI_RETURN(funcName, defVal, ...) \ #define WNEF_EXECUTE_WINAPI_RETURN(funcName, defVal, ...) \
(m_lp##funcName ? m_lp##funcName(__VA_ARGS__) : defVal) (coreData()->m_lp##funcName ? coreData()->m_lp##funcName(__VA_ARGS__) : defVal)
#endif #endif
#endif #endif
@ -180,15 +180,6 @@ using ACCENT_POLICY = struct _ACCENT_POLICY
DWORD AnimationId; DWORD AnimationId;
}; };
const UINT m_defaultDotsPerInch = USER_DEFAULT_SCREEN_DPI;
const qreal m_defaultDevicePixelRatio = 1.0;
// These functions are undocumented APIs so we have to
// load them dynamically unconditionally.
WNEF_GENERATE_WINAPI(GetWindowCompositionAttribute, BOOL, HWND, WINDOWCOMPOSITIONATTRIBDATA *)
WNEF_GENERATE_WINAPI(SetWindowCompositionAttribute, BOOL, HWND, WINDOWCOMPOSITIONATTRIBDATA *)
bool isWin8OrGreater() bool isWin8OrGreater()
{ {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
@ -218,19 +209,6 @@ bool isWin10OrGreater(const int ver)
#endif #endif
} }
bool shouldHaveWindowFrame()
{
#if 0
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10;
#else
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS10;
#endif
#else
return false;
#endif
}
#ifndef WNEF_LINK_SYSLIB #ifndef WNEF_LINK_SYSLIB
// All the following enums, structs and function prototypes are copied from // All the following enums, structs and function prototypes are copied from
@ -403,6 +381,20 @@ using DWM_BLURBEHIND = struct _DWM_BLURBEHIND
BOOL fTransitionOnMaximized; BOOL fTransitionOnMaximized;
}; };
#endif // WNEF_LINK_SYSLIB
// Internal data structure.
using WNEF_CORE_DATA = struct _WNEF_CORE_DATA
{
_WNEF_CORE_DATA() { ResolveWin32APIs(); }
~_WNEF_CORE_DATA() = default;
// These functions are undocumented APIs so we have to
// load them dynamically unconditionally.
WNEF_GENERATE_WINAPI(GetWindowCompositionAttribute, BOOL, HWND, WINDOWCOMPOSITIONATTRIBDATA *)
WNEF_GENERATE_WINAPI(SetWindowCompositionAttribute, BOOL, HWND, WINDOWCOMPOSITIONATTRIBDATA *)
#ifndef WNEF_LINK_SYSLIB
// Some of the following functions are not used by this code anymore, // Some of the following functions are not used by this code anymore,
// but we don't remove them completely because we may still need them later. // but we don't remove them completely because we may still need them later.
WNEF_GENERATE_WINAPI(DwmEnableBlurBehindWindow, HRESULT, HWND, CONST DWM_BLURBEHIND *) WNEF_GENERATE_WINAPI(DwmEnableBlurBehindWindow, HRESULT, HWND, CONST DWM_BLURBEHIND *)
@ -479,8 +471,13 @@ WNEF_GENERATE_WINAPI(DwmGetWindowAttribute, HRESULT, HWND, DWORD, PVOID, DWORD)
WNEF_GENERATE_WINAPI(GetStockObject, HGDIOBJ, int) WNEF_GENERATE_WINAPI(GetStockObject, HGDIOBJ, int)
WNEF_GENERATE_WINAPI(BufferedPaintSetAlpha, HRESULT, HPAINTBUFFER, CONST RECT *, BYTE) WNEF_GENERATE_WINAPI(BufferedPaintSetAlpha, HRESULT, HPAINTBUFFER, CONST RECT *, BYTE)
WNEF_GENERATE_WINAPI(EndBufferedPaint, HRESULT, HPAINTBUFFER, BOOL) WNEF_GENERATE_WINAPI(EndBufferedPaint, HRESULT, HPAINTBUFFER, BOOL)
WNEF_GENERATE_WINAPI( WNEF_GENERATE_WINAPI(BeginBufferedPaint,
BeginBufferedPaint, HPAINTBUFFER, HDC, CONST RECT *, BP_BUFFERFORMAT, BP_PAINTPARAMS *, HDC *) HPAINTBUFFER,
HDC,
CONST RECT *,
BP_BUFFERFORMAT,
BP_PAINTPARAMS *,
HDC *)
WNEF_GENERATE_WINAPI(CreateRectRgnIndirect, HRGN, CONST RECT *) WNEF_GENERATE_WINAPI(CreateRectRgnIndirect, HRGN, CONST RECT *)
WNEF_GENERATE_WINAPI(GetDCEx, HDC, HWND, HRGN, DWORD) WNEF_GENERATE_WINAPI(GetDCEx, HDC, HWND, HRGN, DWORD)
WNEF_GENERATE_WINAPI(GetWindowDC, HDC, HWND) WNEF_GENERATE_WINAPI(GetWindowDC, HDC, HWND)
@ -646,6 +643,34 @@ void ResolveWin32APIs()
#endif // WNEF_LINK_SYSLIB #endif // WNEF_LINK_SYSLIB
int m_borderWidth = -1, m_borderHeight = -1, m_titleBarHeight = -1;
QScopedPointer<WinNativeEventFilter> m_instance;
QList<HWND> m_framelessWindows = {};
};
} // namespace
Q_GLOBAL_STATIC(WNEF_CORE_DATA, coreData)
namespace {
const UINT m_defaultDotsPerInch = USER_DEFAULT_SCREEN_DPI;
const qreal m_defaultDevicePixelRatio = 1.0;
bool shouldHaveWindowFrame()
{
#if 0
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10;
#else
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS10;
#endif
#else
return false;
#endif
}
BOOL IsDwmCompositionEnabled() BOOL IsDwmCompositionEnabled()
{ {
// Since Win8, DWM composition is always enabled and can't be disabled. // Since Win8, DWM composition is always enabled and can't be disabled.
@ -725,7 +750,7 @@ BOOL IsTopLevel(const HWND handle)
BOOL IsApplicationDpiAware() BOOL IsApplicationDpiAware()
{ {
if (m_lpGetProcessDpiAwareness) { if (coreData()->m_lpGetProcessDpiAwareness) {
PROCESS_DPI_AWARENESS awareness = PROCESS_DPI_UNAWARE; PROCESS_DPI_AWARENESS awareness = PROCESS_DPI_UNAWARE;
WNEF_EXECUTE_WINAPI(GetProcessDpiAwareness, WNEF_EXECUTE_WINAPI(GetProcessDpiAwareness,
WNEF_EXECUTE_WINAPI_RETURN(GetCurrentProcess, nullptr), WNEF_EXECUTE_WINAPI_RETURN(GetCurrentProcess, nullptr),
@ -771,12 +796,12 @@ UINT GetDotsPerInchForSystem()
} }
return defaultValue; return defaultValue;
}; };
if (m_lpGetSystemDpiForProcess) { if (coreData()->m_lpGetSystemDpiForProcess) {
return WNEF_EXECUTE_WINAPI_RETURN(GetSystemDpiForProcess, return WNEF_EXECUTE_WINAPI_RETURN(GetSystemDpiForProcess,
0, 0,
WNEF_EXECUTE_WINAPI_RETURN(GetCurrentProcess, nullptr)); WNEF_EXECUTE_WINAPI_RETURN(GetCurrentProcess, nullptr));
} }
if (m_lpGetDpiForSystem) { if (coreData()->m_lpGetDpiForSystem) {
return WNEF_EXECUTE_WINAPI_RETURN(GetDpiForSystem, 0); return WNEF_EXECUTE_WINAPI_RETURN(GetDpiForSystem, 0);
} }
return getScreenDpi(m_defaultDotsPerInch); return getScreenDpi(m_defaultDotsPerInch);
@ -790,10 +815,10 @@ UINT GetDotsPerInchForWindow(const HWND handle)
return m_defaultDotsPerInch; return m_defaultDotsPerInch;
} }
if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) { if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) {
if (m_lpGetDpiForWindow) { if (coreData()->m_lpGetDpiForWindow) {
return WNEF_EXECUTE_WINAPI_RETURN(GetDpiForWindow, 0, handle); return WNEF_EXECUTE_WINAPI_RETURN(GetDpiForWindow, 0, handle);
} }
if (m_lpGetDpiForMonitor) { if (coreData()->m_lpGetDpiForMonitor) {
UINT dpiX = m_defaultDotsPerInch, dpiY = m_defaultDotsPerInch; UINT dpiX = m_defaultDotsPerInch, dpiY = m_defaultDotsPerInch;
WNEF_EXECUTE_WINAPI(GetDpiForMonitor, WNEF_EXECUTE_WINAPI(GetDpiForMonitor,
WNEF_EXECUTE_WINAPI_RETURN(MonitorFromWindow, WNEF_EXECUTE_WINAPI_RETURN(MonitorFromWindow,
@ -878,7 +903,7 @@ RECT GetFrameSizeForWindow(const HWND handle, const BOOL includingTitleBar = FAL
const auto style = WNEF_EXECUTE_WINAPI_RETURN(GetWindowLongPtrW, 0, handle, GWL_STYLE); const auto style = WNEF_EXECUTE_WINAPI_RETURN(GetWindowLongPtrW, 0, handle, GWL_STYLE);
// It's the same with using GetSystemMetrics, the returned values // It's the same with using GetSystemMetrics, the returned values
// of the two functions are identical. // of the two functions are identical.
if (m_lpAdjustWindowRectExForDpi) { if (coreData()->m_lpAdjustWindowRectExForDpi) {
WNEF_EXECUTE_WINAPI(AdjustWindowRectExForDpi, WNEF_EXECUTE_WINAPI(AdjustWindowRectExForDpi,
&rect, &rect,
includingTitleBar ? (style | WS_CAPTION) : (style & ~WS_CAPTION), includingTitleBar ? (style | WS_CAPTION) : (style & ~WS_CAPTION),
@ -954,7 +979,7 @@ int GetSystemMetricsForWindow(const HWND handle, const int index)
{ {
Q_ASSERT(handle); Q_ASSERT(handle);
if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) { if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) {
if (m_lpGetSystemMetricsForDpi) { if (coreData()->m_lpGetSystemMetricsForDpi) {
return WNEF_EXECUTE_WINAPI_RETURN(GetSystemMetricsForDpi, return WNEF_EXECUTE_WINAPI_RETURN(GetSystemMetricsForDpi,
0, 0,
index, index,
@ -1040,7 +1065,6 @@ HWND getHWNDFromQObject(QObject *object)
void updateQtFrame_internal(const HWND handle) void updateQtFrame_internal(const HWND handle)
{ {
Q_ASSERT(handle); Q_ASSERT(handle);
ResolveWin32APIs();
if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) { if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) {
const int tbh = WinNativeEventFilter::getSystemMetric( const int tbh = WinNativeEventFilter::getSystemMetric(
handle, WinNativeEventFilter::SystemMetric::TitleBarHeight); handle, WinNativeEventFilter::SystemMetric::TitleBarHeight);
@ -1064,7 +1088,6 @@ void updateQtFrame_internal(const HWND handle)
bool displaySystemMenu_internal(const HWND handle, const bool isRtl, const LPARAM lParam) bool displaySystemMenu_internal(const HWND handle, const bool isRtl, const LPARAM lParam)
{ {
Q_ASSERT(handle); Q_ASSERT(handle);
ResolveWin32APIs();
if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) { if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) {
const POINT globalMouse{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; const POINT globalMouse{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
POINT localMouse = globalMouse; POINT localMouse = globalMouse;
@ -1089,7 +1112,6 @@ bool displaySystemMenu_internal(const HWND handle, const bool isRtl, const LPARA
QString getCurrentScreenSerialNumber(const HWND handle) QString getCurrentScreenSerialNumber(const HWND handle)
{ {
Q_ASSERT(handle); Q_ASSERT(handle);
ResolveWin32APIs();
if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) { if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, handle)) {
QScreen *currentScreen = nullptr; QScreen *currentScreen = nullptr;
#ifdef QT_WIDGETS_LIB #ifdef QT_WIDGETS_LIB
@ -1112,27 +1134,7 @@ QString getCurrentScreenSerialNumber(const HWND handle)
return {}; return {};
} }
// The standard values of border width, border height and title bar height void install()
// when DPI is 96.
const int m_defaultBorderWidth = 8, m_defaultBorderHeight = 8, m_defaultTitleBarHeight = 30;
// The thickness of an auto-hide taskbar in pixels.
const int kAutoHideTaskbarThicknessPx = 2;
const int kAutoHideTaskbarThicknessPy = kAutoHideTaskbarThicknessPx;
// Internal data structure.
using WNEF_CORE_DATA = struct _WNEF_CORE_DATA
{
int m_borderWidth = -1, m_borderHeight = -1, m_titleBarHeight = -1;
QScopedPointer<WinNativeEventFilter> m_instance;
QList<HWND> m_framelessWindows = {};
};
} // namespace
Q_GLOBAL_STATIC(WNEF_CORE_DATA, coreData)
static void install()
{ {
qCoreAppFixup(); qCoreAppFixup();
if (coreData()->m_instance.isNull()) { if (coreData()->m_instance.isNull()) {
@ -1141,7 +1143,7 @@ static void install()
} }
} }
static void uninstall() void uninstall()
{ {
if (!coreData()->m_instance.isNull()) { if (!coreData()->m_instance.isNull()) {
qApp->removeNativeEventFilter(coreData()->m_instance.data()); qApp->removeNativeEventFilter(coreData()->m_instance.data());
@ -1152,14 +1154,21 @@ static void uninstall()
} }
} }
// The standard values of border width, border height and title bar height
// when DPI is 96.
const int m_defaultBorderWidth = 8, m_defaultBorderHeight = 8, m_defaultTitleBarHeight = 30;
// The thickness of an auto-hide taskbar in pixels.
const int kAutoHideTaskbarThicknessPx = 2;
const int kAutoHideTaskbarThicknessPy = kAutoHideTaskbarThicknessPx;
} // namespace
WinNativeEventFilter::WinNativeEventFilter() WinNativeEventFilter::WinNativeEventFilter()
{ {
ResolveWin32APIs();
qCoreAppFixup(); qCoreAppFixup();
} }
WinNativeEventFilter::~WinNativeEventFilter() = default;
void WinNativeEventFilter::addFramelessWindow(void *window, void WinNativeEventFilter::addFramelessWindow(void *window,
const WINDOWDATA *data, const WINDOWDATA *data,
const bool center, const bool center,
@ -1169,7 +1178,6 @@ void WinNativeEventFilter::addFramelessWindow(void *window,
const int height) const int height)
{ {
Q_ASSERT(window); Q_ASSERT(window);
ResolveWin32APIs();
qCoreAppFixup(); qCoreAppFixup();
const auto hwnd = reinterpret_cast<HWND>(window); const auto hwnd = reinterpret_cast<HWND>(window);
if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd) if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)
@ -2020,7 +2028,6 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
void WinNativeEventFilter::setWindowData(void *window, const WINDOWDATA *data) void WinNativeEventFilter::setWindowData(void *window, const WINDOWDATA *data)
{ {
Q_ASSERT(window); Q_ASSERT(window);
ResolveWin32APIs();
const auto hwnd = reinterpret_cast<HWND>(window); const auto hwnd = reinterpret_cast<HWND>(window);
if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd) && data) { if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd) && data) {
createUserData(hwnd, data); createUserData(hwnd, data);
@ -2036,7 +2043,6 @@ void WinNativeEventFilter::setWindowData(QObject *window, const WINDOWDATA *data
WinNativeEventFilter::WINDOWDATA *WinNativeEventFilter::windowData(void *window) WinNativeEventFilter::WINDOWDATA *WinNativeEventFilter::windowData(void *window)
{ {
Q_ASSERT(window); Q_ASSERT(window);
ResolveWin32APIs();
const auto hwnd = reinterpret_cast<HWND>(window); const auto hwnd = reinterpret_cast<HWND>(window);
if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) { if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) {
createUserData(hwnd); createUserData(hwnd);
@ -2072,7 +2078,6 @@ void WinNativeEventFilter::updateWindow(void *handle,
const bool redraw) const bool redraw)
{ {
Q_ASSERT(handle); Q_ASSERT(handle);
ResolveWin32APIs();
const auto hwnd = reinterpret_cast<HWND>(handle); const auto hwnd = reinterpret_cast<HWND>(handle);
if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) { if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) {
if (triggerFrameChange) { if (triggerFrameChange) {
@ -2101,7 +2106,6 @@ int WinNativeEventFilter::getSystemMetric(void *handle,
const bool dpiAware) const bool dpiAware)
{ {
Q_ASSERT(handle); Q_ASSERT(handle);
ResolveWin32APIs();
const auto hwnd = reinterpret_cast<HWND>(handle); const auto hwnd = reinterpret_cast<HWND>(handle);
const qreal dpr = dpiAware ? GetDevicePixelRatioForWindow(hwnd) : m_defaultDevicePixelRatio; const qreal dpr = dpiAware ? GetDevicePixelRatioForWindow(hwnd) : m_defaultDevicePixelRatio;
if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) { if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) {
@ -2179,7 +2183,6 @@ void WinNativeEventFilter::setWindowGeometry(
void *handle, const int x, const int y, const int width, const int height) void *handle, const int x, const int y, const int width, const int height)
{ {
Q_ASSERT(handle); Q_ASSERT(handle);
ResolveWin32APIs();
const auto hwnd = reinterpret_cast<HWND>(handle); const auto hwnd = reinterpret_cast<HWND>(handle);
if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd) && (x > 0) && (y > 0) && (width > 0) if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd) && (x > 0) && (y > 0) && (width > 0)
&& (height > 0)) { && (height > 0)) {
@ -2195,7 +2198,6 @@ void WinNativeEventFilter::setWindowGeometry(
void WinNativeEventFilter::moveWindowToDesktopCenter(void *handle) void WinNativeEventFilter::moveWindowToDesktopCenter(void *handle)
{ {
Q_ASSERT(handle); Q_ASSERT(handle);
ResolveWin32APIs();
const auto hwnd = reinterpret_cast<HWND>(handle); const auto hwnd = reinterpret_cast<HWND>(handle);
if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) { if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) {
const WINDOWINFO windowInfo = GetInfoForWindow(hwnd); const WINDOWINFO windowInfo = GetInfoForWindow(hwnd);
@ -2253,7 +2255,6 @@ bool WinNativeEventFilter::displaySystemMenu(void *handle,
const int y) const int y)
{ {
Q_ASSERT(handle); Q_ASSERT(handle);
ResolveWin32APIs();
const auto hwnd = reinterpret_cast<HWND>(handle); const auto hwnd = reinterpret_cast<HWND>(handle);
if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) { if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) {
const HMENU hMenu = WNEF_EXECUTE_WINAPI_RETURN(GetSystemMenu, nullptr, hwnd, FALSE); const HMENU hMenu = WNEF_EXECUTE_WINAPI_RETURN(GetSystemMenu, nullptr, hwnd, FALSE);
@ -2303,7 +2304,6 @@ bool WinNativeEventFilter::displaySystemMenu(void *handle,
bool WinNativeEventFilter::setAcrylicEffectEnabled(void *handle, const bool enabled) bool WinNativeEventFilter::setAcrylicEffectEnabled(void *handle, const bool enabled)
{ {
Q_ASSERT(handle); Q_ASSERT(handle);
ResolveWin32APIs();
const auto hwnd = reinterpret_cast<HWND>(handle); const auto hwnd = reinterpret_cast<HWND>(handle);
if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) { if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) {
DWM_BLURBEHIND dwmBB; DWM_BLURBEHIND dwmBB;

View File

@ -64,7 +64,7 @@ public:
enum class SystemMetric { BorderWidth, BorderHeight, TitleBarHeight }; enum class SystemMetric { BorderWidth, BorderHeight, TitleBarHeight };
explicit WinNativeEventFilter(); explicit WinNativeEventFilter();
~WinNativeEventFilter() override; ~WinNativeEventFilter() override = default;
// Make the given window become frameless. // Make the given window become frameless.
// The width and height will be scaled automatically according to DPI. Don't // The width and height will be scaled automatically according to DPI. Don't