Compare commits

..
18 Commits
Author SHA1 Message Date
deepend 35a8f6cbd5 fix application crash + other fixes 2026-09-11 03:37:28 -06:00
deepend 8714af4297 enable storage information 2026-09-03 18:00:20 -06:00
deepend f1a053e094 fix Win32 IP Helper includes for sysinfo 2026-09-03 17:21:00 -06:00
deepend 71c08c2c2b bring sysinfo into 2026 2026-09-03 15:19:21 -06:00
deepend bb3893d36c Fix channel ACTION routing after self-message handling 2026-09-03 09:27:07 -06:00
deepend-tildeclub fe57449f6c Merge pull request #374 from TehPeGaSuS/fix/weechat-relay-pm-split
Fix private messages splitting into two windows over WeeChat's IRC relay
2026-09-03 08:20:55 -07:00
ThePeGaSuS c725adaa09 Fix CTCP ACTION not rerouting self-sent /me to correct PM window
Mirrors the PRIVMSG fix: replayed /me messages sent by our own nick
via WeeChat relay backlog were always passed with fromme=FALSE,
so they never got rerouted to the actual PM query window.
2026-09-03 08:51:21 +02:00
deepend-tildeclub 7731bfa4e0 Merge pull request #377 from ZoiteChat/retain-searched-text-view
unblock navigation when in search
2026-09-02 09:46:13 -07:00
deepend e72546aaec fix accidental removal of mg_apply_emoji_fallback_widget (entry); 2026-09-02 10:43:09 -06:00
deepend d6d648f0f4 unblock navigation when in search 2026-09-02 10:41:21 -06:00
deepend-tildeclub 56fddd56f5 Merge pull request #340 from bgermann/winspell
Require Enchant 2.8.19+ and drop win8 provider
2026-09-01 15:06:11 -07:00
Bastian Germann 3c9c350bc0 win32: Set Enchant prefix dir to install location
Enchant looks for its providers (including the WinSpell spellcheck
provider) under a hardcoded default path derived from its build
prefix, which is C:\gtk-build\gtk\x64\release\lib\enchant-2 when
built via gvsbuild. After installation, the provider DLLs live
under ZoiteChat's own install directory, so Enchant fails to find
them and spellcheck silently stops working.

Call enchant_set_prefix_dir() to redirect Enchant's provider search
to <install_dir> before the first enchant_broker_init().
enchant_set_prefix_dir() expects the installation prefix itself;
Enchant adds lib\enchant-2 when searching for providers.
The symbol is loaded optionally since it may not exist in older
Enchant builds; if missing, behavior is unchanged.
2026-09-01 21:36:40 +02:00
Bastian Germann 8340a04bd6 win32: Use parentheses to be independent of and/or evaluation order
The Version.NTPlatform shall be true for every evaluation of the if
branch.
2026-08-31 14:45:31 +02:00
Bastian Germann ccef70fd3d Clarify comments about spelling provider 2026-08-31 14:29:16 +02:00
Bastian Germann 0650fa7bbe win32: Reference enchant-2 in installer 2026-08-31 14:17:58 +02:00
Bastian Germann 5cffa06385 Update gvsbuild to version with updated enchant 2026-08-31 14:17:58 +02:00
Bastian Germann 4df17a151e Require Enchant 2.8.19+ and drop win8 provider
Require Enchant 2.8.19 or newer so Windows can use upstream winspell.
Remove the in-tree libenchant_win8 provider project and sources.
Update runtime library probing to prefer Enchant 2 library names.
2026-08-31 14:17:58 +02:00
ThePeGaSuS 0ba523d071 Fix: private messages split into two windows over WeeChat's IRC relay
When you send a private message, ZoiteChat sometimes gets the same
message echoed back by the server so it can show it in the right
window. Whether it recognizes that echo depended on the server first
confirming a feature called "echo-message" during connection setup.

WeeChat's relay doesn't seem to confirm that feature the way other
servers (or ZNC) do, so ZoiteChat didn't recognize its own echoed
messages and treated them as brand new incoming messages from
yourself, opening a second, separate conversation window instead of
using the one you were already typing in.

This makes ZoiteChat recognize its own message by checking who sent
it, instead of only trusting that feature confirmation. That way,
sent and received messages always end up in the same conversation
window, regardless of how well the server/relay announces its
capabilities.
2026-08-28 20:48:12 +02:00
20 changed files with 1995 additions and 563 deletions
+1 -1
View File
@@ -66,7 +66,7 @@ jobs:
Download-WithRetry -Url https://github.com/jrsoftware/issrc/releases/download/is-6_7_1/innosetup-6.7.1.exe -OutFile deps\innosetup-unicode.exe
& deps\innosetup-unicode.exe /VERYSILENT | Out-Null
Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.18.1/GTK3_Gvsbuild_zoitechat-2.18.1_x64.zip -OutFile deps\gtk-${{ matrix.arch }}.zip
Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.20.0-alpha.1/GTK3_Gvsbuild_zoitechat-2.20.0-alpha.1_x64.zip -OutFile deps\gtk-${{ matrix.arch }}.zip
Expand-Archive -LiteralPath deps\gtk-${{ matrix.arch }}.zip -DestinationPath C:\gtk-build\gtk\x64\release -Force
Download-WithRetry -Url https://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-hicolor-icon-theme-0.18-1-any.pkg.tar.zst -OutFile deps\hicolor-icon-theme.pkg.tar.zst
+63 -34
View File
@@ -6,50 +6,79 @@ sysinfo_sources = [
sysinfo_deps = [
libgio_dep,
zoitechat_plugin_dep,
common_sysinfo_deps,
]
sysinfo_includes = []
sysinfo_cargs = ['-DHAVE_CONFIG_H']
system = host_machine.system()
if system == 'linux' or system == 'gnu' or system.startswith('gnu/') or system == 'darwin' or system == 'freebsd'
sysinfo_includes += 'shared'
sysinfo_sources += [
'shared/df.c'
]
if system == 'linux' or system == 'gnu' or system.startswith('gnu/') or system == 'freebsd'
libpci = dependency('libpci', required: false, method: 'pkg-config')
if libpci.found()
sysinfo_deps += libpci
sysinfo_cargs += '-DHAVE_LIBPCI'
sysinfo_sources += 'unix/pci.c'
picidsdir = libpci.get_pkgconfig_variable('idsdir')
pciids = join_paths(picidsdir, 'pci.ids')
sysinfo_cargs += '-DPCIIDS_FILE="@0@"'.format(pciids)
endif
if get_option('gtk-frontend')
sysinfo_deps += dependency('gtk+-3.0', version: '>= 3.22')
endif
sysinfo_includes += 'unix'
sysinfo_sources += [
'unix/backend.c',
'unix/match.c',
'unix/parse.c',
]
elif system == 'darwin'
add_languages('objc')
sysinfo_sources += 'osx/backend.m'
if system == 'linux'
if get_option('gtk-frontend')
sysinfo_deps += dependency('gtk+-3.0', version: '>= 3.22')
endif
elif system == 'windows'
libpci = dependency('libpci', required: false, method: 'pkg-config')
if libpci.found()
sysinfo_deps += libpci
sysinfo_cargs += '-DHAVE_LIBPCI'
endif
sysinfo_includes += 'unix'
sysinfo_sources += 'unix/modern-backend.c'
sysinfo_test_deps = [libgio_dep]
sysinfo_test_cargs = []
if libpci.found()
sysinfo_test_deps += libpci
sysinfo_test_cargs += '-DHAVE_LIBPCI'
endif
sysinfo_backend_tests = executable('sysinfo_backend_tests',
['tests/test-linux-backend.c', 'format.c'],
dependencies: sysinfo_test_deps,
c_args: sysinfo_test_cargs,
)
test('Sysinfo Backend Tests', sysinfo_backend_tests, protocol: 'tap')
elif system == 'freebsd' or system == 'gnu' or system.startswith('gnu/')
sysinfo_includes += ['shared', 'unix']
sysinfo_sources += [
'win32/backend.c',
'../../src/common/sysinfo/win32/backend.c'
'shared/df.c',
'unix/backend.c',
'unix/match.c',
'unix/parse.c',
]
libpci = dependency('libpci', required: false, method: 'pkg-config')
if libpci.found()
sysinfo_deps += libpci
sysinfo_cargs += '-DHAVE_LIBPCI'
sysinfo_sources += 'unix/pci.c'
picidsdir = libpci.get_variable(pkgconfig: 'idsdir')
pciids = join_paths(picidsdir, 'pci.ids')
sysinfo_cargs += '-DPCIIDS_FILE="@0@"'.format(pciids)
endif
if get_option('gtk-frontend')
sysinfo_deps += dependency('gtk+-3.0', version: '>= 3.22')
endif
elif system == 'darwin'
sysinfo_includes += 'shared'
sysinfo_sources += 'shared/df.c'
add_languages('objc')
sysinfo_sources += 'osx/backend.m'
elif system == 'windows'
cc = meson.get_compiler('c')
sysinfo_deps += [
cc.find_library('advapi32', required: true),
cc.find_library('dxgi', required: true),
cc.find_library('dxguid', required: true),
cc.find_library('iphlpapi', required: true),
cc.find_library('winmm', required: true),
]
sysinfo_sources += 'win32/backend.c'
else
error('sysinfo: Unknown system?')
endif
+29 -8
View File
@@ -40,8 +40,8 @@ static zoitechat_plugin *ph;
static char name[] = "Sysinfo";
static char desc[] = "Display info about your hardware and OS";
static char version[] = "1.0";
static char sysinfo_help[] = "SysInfo Usage:\n /SYSINFO [-e|-o] [CLIENT|UI|OS|CPU|RAM|STORAGE|GPU|CHIPSET|SOUND|ETHERNET|UPTIME], print various details about your system or print a summary without arguments\n /SYSINFO SET <variable>\n";
static char version[] = "2.0";
static char sysinfo_help[] = "SysInfo Usage:\n /SYSINFO [-e|-o] [CLIENT|UI|OS|CPU|RAM|MEMORY|STORAGE|DISK|GPU|VGA|CHIPSET|SOUND|NETWORK|ETHERNET|UPTIME], print various details about your system or print a summary without arguments\n /SYSINFO SET <variable>\n";
typedef struct
{
@@ -68,11 +68,11 @@ static hwinfo hwinfos[] = {
{"os", "OS", sysinfo_backend_get_os},
{"cpu", "CPU", sysinfo_backend_get_cpu},
{"memory", "Memory", sysinfo_backend_get_memory},
{"storage", "Storage", sysinfo_backend_get_disk, TRUE},
{"storage", "Storage", sysinfo_backend_get_disk},
{"gpu", "GPU", sysinfo_backend_get_gpu},
{"chipset", "CHIPSET", sysinfo_backend_get_chipset, TRUE},
{"chipset", "Chipset", sysinfo_backend_get_chipset, TRUE},
{"sound", "Sound", sysinfo_backend_get_sound, TRUE},
{"ethernet", "Ethernet", sysinfo_backend_get_network, TRUE},
{"ethernet", "Network", sysinfo_backend_get_network, TRUE},
{"uptime", "Uptime", sysinfo_backend_get_uptime},
{NULL, NULL},
};
@@ -115,14 +115,30 @@ print_summary (gboolean announce)
g_free (output);
}
static const char *
normalize_info_name (const char *info)
{
if (!g_ascii_strcasecmp (info, "ram"))
return "memory";
if (!g_ascii_strcasecmp (info, "disk"))
return "storage";
if (!g_ascii_strcasecmp (info, "vga"))
return "gpu";
if (!g_ascii_strcasecmp (info, "network"))
return "ethernet";
return info;
}
static void
print_info (char *info, gboolean announce)
{
const char *normalized = normalize_info_name (info);
int i;
for (i = 0; hwinfos[i].name != NULL; i++)
{
if (!g_ascii_strcasecmp (info, hwinfos[i].name))
if (!g_ascii_strcasecmp (normalized, hwinfos[i].name))
{
char *str = hwinfos[i].callback();
if (str)
@@ -183,12 +199,17 @@ sysinfo_set_pref (char *key, char *value)
}
else if (g_str_has_prefix (key, "hide_"))
{
const char *normalized = normalize_info_name (key + 5);
int i;
for (i = 0; hwinfos[i].name != NULL; i++)
{
if (!strcmp (key + 5, hwinfos[i].name))
if (!strcmp (normalized, hwinfos[i].name))
{
sysinfo_set_pref_real (key, value, hwinfos[i].def);
char canonical[32];
g_snprintf (canonical, sizeof (canonical), "hide_%s", hwinfos[i].name);
sysinfo_set_pref_real (canonical, value, hwinfos[i].def);
return;
}
}
File diff suppressed because it is too large Load Diff
+709 -83
View File
@@ -1,28 +1,34 @@
/* ZoiteChat
* Copyright (c) 2011-2012 Berke Viktor.
/*
* Modern Windows SysInfo backend for ZoiteChat.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* Avoids WMI in the plugin hot path and uses native Win32/DXGI APIs.
*/
#include <stdio.h>
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0601
#elif _WIN32_WINNT < 0x0601
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0601
#endif
#include <winsock2.h>
#include <windows.h>
#include <wbemidl.h>
#include <winternl.h>
#include <dxgi.h>
#include <iphlpapi.h>
#include <ipifcons.h>
#include <mmsystem.h>
#ifdef _MSC_VER
#pragma comment(lib, "advapi32.lib")
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "winmm.lib")
#endif
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <glib.h>
@@ -30,63 +36,548 @@
#include "config.h"
#endif
#include "../../../src/common/sysinfo/sysinfo.h"
#include "../format.h"
#include "../sysinfo-backend.h"
static char *get_memory_info (void);
#define SYSINFO_SEP " \xC2\xB7 "
char *
sysinfo_backend_get_sound (void)
typedef struct
{
return NULL;
}
char *os;
char *cpu;
char *gpu;
char *sound;
} WinStaticSnapshot;
char *
sysinfo_backend_get_network (void)
static WinStaticSnapshot snapshot;
static gsize os_initialized = 0;
static gsize cpu_initialized = 0;
static gsize gpu_initialized = 0;
static gsize sound_initialized = 0;
static char *wide_to_utf8 (const WCHAR *value);
static char *read_registry_string (HKEY root, const WCHAR *subkey, const WCHAR *value_name);
static gboolean read_registry_dword (HKEY root, const WCHAR *subkey, const WCHAR *value_name, DWORD *value);
static const char *get_native_arch (void);
static DWORD get_windows_build_number (void);
static char *build_os_string (void);
static DWORD count_physical_cores (void);
static char *build_cpu_string (void);
static char *build_gpu_string (void);
static char *build_sound_string (void);
static void ensure_os_snapshot (void);
static void ensure_cpu_snapshot (void);
static void ensure_gpu_snapshot (void);
static void ensure_sound_snapshot (void);
static char *format_usage (guint64 total, guint64 available);
static char *join_ptr_array (GPtrArray *items, const char *separator);
static void ptr_array_add_unique (GPtrArray *items, const char *value);
static gboolean adapter_looks_virtual (const char *name);
static char *
wide_to_utf8 (const WCHAR *value)
{
return NULL;
}
char *
sysinfo_backend_get_uptime (void)
{
return sysinfo_format_uptime (GetTickCount64 () / 1000);
}
char *
sysinfo_backend_get_disk (void)
{
guint64 hdd_capacity;
guint64 hdd_free_space;
sysinfo_get_hdd_info (&hdd_capacity, &hdd_free_space);
if (hdd_capacity != 0)
if (!value || value[0] == L'\0')
{
return sysinfo_format_disk(hdd_capacity, hdd_free_space);
return NULL;
}
return NULL;
return g_utf16_to_utf8 ((const gunichar2 *) value, -1, NULL, NULL, NULL);
}
static char *
read_registry_string (HKEY root, const WCHAR *subkey, const WCHAR *value_name)
{
HKEY key;
DWORD type = 0;
DWORD size = 0;
WCHAR *buffer;
char *result = NULL;
LONG status;
status = RegOpenKeyExW (root, subkey, 0, KEY_READ | KEY_WOW64_64KEY, &key);
if (status != ERROR_SUCCESS)
{
return NULL;
}
status = RegQueryValueExW (key, value_name, NULL, &type, NULL, &size);
if (status != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ) || size < sizeof (WCHAR))
{
RegCloseKey (key);
return NULL;
}
buffer = g_malloc0 ((gsize) size + sizeof (WCHAR));
status = RegQueryValueExW (key, value_name, NULL, &type, (LPBYTE) buffer, &size);
if (status == ERROR_SUCCESS)
{
result = wide_to_utf8 (buffer);
if (result)
{
g_strstrip (result);
}
}
g_free (buffer);
RegCloseKey (key);
return result;
}
static gboolean
read_registry_dword (HKEY root, const WCHAR *subkey, const WCHAR *value_name, DWORD *value)
{
HKEY key;
DWORD type = 0;
DWORD size = sizeof (*value);
LONG status;
status = RegOpenKeyExW (root, subkey, 0, KEY_READ | KEY_WOW64_64KEY, &key);
if (status != ERROR_SUCCESS)
{
return FALSE;
}
status = RegQueryValueExW (key, value_name, NULL, &type, (LPBYTE) value, &size);
RegCloseKey (key);
return status == ERROR_SUCCESS && type == REG_DWORD;
}
static const char *
get_native_arch (void)
{
SYSTEM_INFO info;
GetNativeSystemInfo (&info);
switch (info.wProcessorArchitecture)
{
case PROCESSOR_ARCHITECTURE_AMD64:
return "x64";
case PROCESSOR_ARCHITECTURE_ARM64:
return "ARM64";
case PROCESSOR_ARCHITECTURE_INTEL:
return "x86";
default:
return "unknown-arch";
}
}
static DWORD
get_windows_build_number (void)
{
typedef LONG (WINAPI *RtlGetVersionFn) (PRTL_OSVERSIONINFOW);
HMODULE ntdll;
RtlGetVersionFn rtl_get_version;
RTL_OSVERSIONINFOW version;
DWORD build = 0;
ntdll = GetModuleHandleW (L"ntdll.dll");
if (ntdll)
{
rtl_get_version = (RtlGetVersionFn) GetProcAddress (ntdll, "RtlGetVersion");
if (rtl_get_version)
{
ZeroMemory (&version, sizeof (version));
version.dwOSVersionInfoSize = sizeof (version);
if (rtl_get_version (&version) == 0)
{
build = version.dwBuildNumber;
}
}
}
return build;
}
static char *
build_os_string (void)
{
static const WCHAR current_version_key[] = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
char *product;
char *display_version;
char *registry_build;
char *fixed_product = NULL;
char *result;
DWORD ubr = 0;
DWORD build;
guint64 parsed_build = 0;
product = read_registry_string (HKEY_LOCAL_MACHINE, current_version_key, L"ProductName");
display_version = read_registry_string (HKEY_LOCAL_MACHINE, current_version_key, L"DisplayVersion");
registry_build = read_registry_string (HKEY_LOCAL_MACHINE, current_version_key, L"CurrentBuildNumber");
read_registry_dword (HKEY_LOCAL_MACHINE, current_version_key, L"UBR", &ubr);
build = get_windows_build_number ();
if (registry_build)
{
parsed_build = g_ascii_strtoull (registry_build, NULL, 10);
if (parsed_build != 0)
{
build = (DWORD) parsed_build;
}
}
if (!product)
{
product = g_strdup (build >= 22000 ? "Windows 11" : "Windows");
}
else if (build >= 22000 && g_str_has_prefix (product, "Windows 10"))
{
fixed_product = g_strdup_printf ("Windows 11%s", product + strlen ("Windows 10"));
g_free (product);
product = fixed_product;
}
if (display_version && build != 0)
{
result = g_strdup_printf ("%s %s" SYSINFO_SEP "build %lu.%lu" SYSINFO_SEP "%s",
product, display_version, (unsigned long) build, (unsigned long) ubr, get_native_arch ());
}
else if (build != 0)
{
result = g_strdup_printf ("%s" SYSINFO_SEP "build %lu.%lu" SYSINFO_SEP "%s",
product, (unsigned long) build, (unsigned long) ubr, get_native_arch ());
}
else
{
result = g_strdup_printf ("%s" SYSINFO_SEP "%s", product, get_native_arch ());
}
g_free (product);
g_free (display_version);
g_free (registry_build);
return result;
}
static DWORD
count_physical_cores (void)
{
DWORD length = 0;
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX buffer;
BYTE *cursor;
BYTE *end;
DWORD cores = 0;
GetLogicalProcessorInformationEx (RelationProcessorCore, NULL, &length);
if (GetLastError () != ERROR_INSUFFICIENT_BUFFER || length == 0)
{
return 0;
}
buffer = g_malloc0 (length);
if (!GetLogicalProcessorInformationEx (RelationProcessorCore, buffer, &length))
{
g_free (buffer);
return 0;
}
cursor = (BYTE *) buffer;
end = cursor + length;
while (cursor < end)
{
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) cursor;
if (info->Size == 0)
{
break;
}
if (info->Relationship == RelationProcessorCore)
{
cores++;
}
cursor += info->Size;
}
g_free (buffer);
return cores;
}
static char *
build_cpu_string (void)
{
static const WCHAR cpu_key[] = L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
char *name;
DWORD cores;
DWORD threads;
SYSTEM_INFO info;
char *result;
name = read_registry_string (HKEY_LOCAL_MACHINE, cpu_key, L"ProcessorNameString");
if (!name)
{
name = g_strdup ("Unknown CPU");
}
cores = count_physical_cores ();
threads = GetActiveProcessorCount (ALL_PROCESSOR_GROUPS);
if (threads == 0)
{
GetNativeSystemInfo (&info);
threads = info.dwNumberOfProcessors;
}
if (cores != 0)
{
result = g_strdup_printf ("%s" SYSINFO_SEP "%luC/%luT",
name, (unsigned long) cores, (unsigned long) threads);
}
else
{
/* Logical processor count cannot establish the physical core count. */
result = g_strdup_printf ("%s" SYSINFO_SEP "%luT", name, (unsigned long) threads);
}
g_free (name);
return result;
}
static char *
join_ptr_array (GPtrArray *items, const char *separator)
{
GString *joined;
guint i;
if (!items || items->len == 0)
{
return NULL;
}
joined = g_string_new (NULL);
for (i = 0; i < items->len; i++)
{
if (i != 0)
{
g_string_append (joined, separator);
}
g_string_append (joined, g_ptr_array_index (items, i));
}
return g_string_free (joined, FALSE);
}
static void
ptr_array_add_unique (GPtrArray *items, const char *value)
{
guint i;
if (!value || value[0] == '\0')
{
return;
}
for (i = 0; i < items->len; i++)
{
if (g_ascii_strcasecmp (g_ptr_array_index (items, i), value) == 0)
{
return;
}
}
g_ptr_array_add (items, g_strdup (value));
}
static char *
build_gpu_string (void)
{
IDXGIFactory1 *factory = NULL;
GPtrArray *adapters;
UINT index;
HRESULT hr;
char *result;
hr = CreateDXGIFactory1 (&IID_IDXGIFactory1, (void **) &factory);
if (FAILED (hr) || !factory)
{
return NULL;
}
adapters = g_ptr_array_new_with_free_func (g_free);
for (index = 0; ; index++)
{
IDXGIAdapter1 *adapter = NULL;
DXGI_ADAPTER_DESC1 desc;
char *name;
char *label;
hr = factory->lpVtbl->EnumAdapters1 (factory, index, &adapter);
if (hr == DXGI_ERROR_NOT_FOUND)
{
break;
}
if (FAILED (hr) || !adapter)
{
/* A failed enumeration is not another adapter. Retrying every
* index can freeze the client indefinitely after a device error. */
break;
}
ZeroMemory (&desc, sizeof (desc));
hr = adapter->lpVtbl->GetDesc1 (adapter, &desc);
adapter->lpVtbl->Release (adapter);
if (FAILED (hr) || (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE))
{
continue;
}
name = wide_to_utf8 (desc.Description);
if (!name)
{
continue;
}
g_strstrip (name);
if ((guint64) desc.DedicatedVideoMemory >= (512ULL * 1024ULL * 1024ULL))
{
char *vram = g_format_size_full ((guint64) desc.DedicatedVideoMemory, G_FORMAT_SIZE_IEC_UNITS);
label = g_strdup_printf ("%s (%s VRAM)", name, vram);
g_free (vram);
}
else
{
label = g_strdup (name);
}
ptr_array_add_unique (adapters, label);
g_free (label);
g_free (name);
}
factory->lpVtbl->Release (factory);
result = join_ptr_array (adapters, ", ");
g_ptr_array_free (adapters, TRUE);
return result;
}
static char *
build_sound_string (void)
{
UINT count;
UINT i;
GPtrArray *devices;
char *result;
count = waveOutGetNumDevs ();
if (count == 0)
{
return NULL;
}
devices = g_ptr_array_new_with_free_func (g_free);
for (i = 0; i < count; i++)
{
WAVEOUTCAPSW caps;
char *name;
ZeroMemory (&caps, sizeof (caps));
if (waveOutGetDevCapsW ((UINT_PTR) i, &caps, sizeof (caps)) != MMSYSERR_NOERROR)
{
continue;
}
name = wide_to_utf8 (caps.szPname);
if (name)
{
g_strstrip (name);
ptr_array_add_unique (devices, name);
g_free (name);
}
}
result = join_ptr_array (devices, ", ");
g_ptr_array_free (devices, TRUE);
return result;
}
static void
ensure_os_snapshot (void)
{
if (g_once_init_enter (&os_initialized))
{
snapshot.os = build_os_string ();
g_once_init_leave (&os_initialized, 1);
}
}
static void
ensure_cpu_snapshot (void)
{
if (g_once_init_enter (&cpu_initialized))
{
snapshot.cpu = build_cpu_string ();
g_once_init_leave (&cpu_initialized, 1);
}
}
static void
ensure_gpu_snapshot (void)
{
if (g_once_init_enter (&gpu_initialized))
{
snapshot.gpu = build_gpu_string ();
g_once_init_leave (&gpu_initialized, 1);
}
}
static void
ensure_sound_snapshot (void)
{
if (g_once_init_enter (&sound_initialized))
{
snapshot.sound = build_sound_string ();
g_once_init_leave (&sound_initialized, 1);
}
}
static char *
format_usage (guint64 total, guint64 available)
{
guint64 used;
char *used_fmt;
char *total_fmt;
char *result;
double percent;
if (total == 0)
{
return NULL;
}
if (available > total)
{
available = total;
}
used = total - available;
percent = ((double) used / (double) total) * 100.0;
used_fmt = g_format_size_full (used, G_FORMAT_SIZE_IEC_UNITS);
total_fmt = g_format_size_full (total, G_FORMAT_SIZE_IEC_UNITS);
result = g_strdup_printf ("%s / %s used (%.0f%%)", used_fmt, total_fmt, percent);
g_free (used_fmt);
g_free (total_fmt);
return result;
}
char *
sysinfo_backend_get_os (void)
{
ensure_os_snapshot ();
return g_strdup (snapshot.os);
}
char *
sysinfo_backend_get_cpu (void)
{
return sysinfo_get_cpu ();
}
char *
sysinfo_backend_get_memory (void)
{
/* Memory information is always loaded dynamically since it includes the current amount of free memory */
return get_memory_info ();
ensure_cpu_snapshot ();
return g_strdup (snapshot.cpu);
}
char *
sysinfo_backend_get_gpu (void)
{
return sysinfo_get_gpu ();
ensure_gpu_snapshot ();
return g_strdup (snapshot.gpu);
}
char *
sysinfo_backend_get_sound (void)
{
ensure_sound_snapshot ();
return g_strdup (snapshot.sound);
}
char *
@@ -96,43 +587,178 @@ sysinfo_backend_get_chipset (void)
}
char *
sysinfo_backend_get_os (void)
sysinfo_backend_get_memory (void)
{
return sysinfo_get_os ();
}
MEMORYSTATUSEX memory;
static char *get_memory_info (void)
{
MEMORYSTATUSEX meminfo = { 0 };
meminfo.dwLength = sizeof (meminfo);
if (!GlobalMemoryStatusEx (&meminfo))
ZeroMemory (&memory, sizeof (memory));
memory.dwLength = sizeof (memory);
if (!GlobalMemoryStatusEx (&memory))
{
return NULL;
}
return sysinfo_format_memory (meminfo.ullTotalPhys, meminfo.ullAvailPhys);
return format_usage ((guint64) memory.ullTotalPhys, (guint64) memory.ullAvailPhys);
}
static const char *sysinfo_detect_toolkit(void)
char *
sysinfo_backend_get_disk (void)
{
#if defined(USE_GTK_FRONTEND)
return "GTK3";
#else
return NULL;
#endif
WCHAR windows_dir[MAX_PATH];
WCHAR volume_path[MAX_PATH];
ULARGE_INTEGER available;
ULARGE_INTEGER total;
ULARGE_INTEGER total_free;
char *usage;
char *volume_utf8;
char *result;
if (GetWindowsDirectoryW (windows_dir, G_N_ELEMENTS (windows_dir)) == 0)
{
return NULL;
}
if (!GetVolumePathNameW (windows_dir, volume_path, G_N_ELEMENTS (volume_path)))
{
return NULL;
}
if (!GetDiskFreeSpaceExW (volume_path, &available, &total, &total_free))
{
return NULL;
}
usage = format_usage ((guint64) total.QuadPart, (guint64) available.QuadPart);
if (!usage)
{
return NULL;
}
volume_utf8 = wide_to_utf8 (volume_path);
if (!volume_utf8)
{
return usage;
}
g_strchomp (volume_utf8);
result = g_strdup_printf ("%s" SYSINFO_SEP "%s", usage, volume_utf8);
g_free (volume_utf8);
g_free (usage);
return result;
}
char *
sysinfo_backend_get_uptime (void)
{
return sysinfo_format_uptime ((gint64) (GetTickCount64 () / 1000ULL));
}
static gboolean
adapter_looks_virtual (const char *name)
{
char *lower;
gboolean result;
if (!name)
{
return FALSE;
}
lower = g_ascii_strdown (name, -1);
result = strstr (lower, "virtual") != NULL ||
strstr (lower, "hyper-v") != NULL ||
strstr (lower, "vmware") != NULL ||
strstr (lower, "virtualbox") != NULL ||
strstr (lower, "tap-windows") != NULL ||
strstr (lower, "wireguard") != NULL ||
strstr (lower, "vpn") != NULL;
g_free (lower);
return result;
}
char *
sysinfo_backend_get_network (void)
{
ULONG size = 16 * 1024;
ULONG status;
IP_ADAPTER_ADDRESSES *addresses;
IP_ADAPTER_ADDRESSES *adapter;
GPtrArray *physical;
GPtrArray *fallback;
GPtrArray *selected;
char *result;
addresses = g_malloc0 (size);
status = GetAdaptersAddresses (AF_UNSPEC,
GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER,
NULL, addresses, &size);
if (status == ERROR_BUFFER_OVERFLOW)
{
g_free (addresses);
addresses = g_malloc0 (size);
status = GetAdaptersAddresses (AF_UNSPEC,
GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER,
NULL, addresses, &size);
}
if (status != NO_ERROR)
{
g_free (addresses);
return NULL;
}
physical = g_ptr_array_new_with_free_func (g_free);
fallback = g_ptr_array_new_with_free_func (g_free);
for (adapter = addresses; adapter != NULL; adapter = adapter->Next)
{
char *description;
char *friendly;
const char *name;
const char *type;
char *label;
if (adapter->OperStatus != IfOperStatusUp || adapter->PhysicalAddressLength == 0)
{
continue;
}
if (adapter->IfType != IF_TYPE_ETHERNET_CSMACD && adapter->IfType != IF_TYPE_IEEE80211)
{
continue;
}
description = wide_to_utf8 (adapter->Description);
friendly = wide_to_utf8 (adapter->FriendlyName);
name = description && description[0] ? description : friendly;
if (!name)
{
g_free (description);
g_free (friendly);
continue;
}
type = adapter->IfType == IF_TYPE_IEEE80211 ? "Wi-Fi" : "Ethernet";
label = g_strdup_printf ("%s: %s", type, name);
ptr_array_add_unique (fallback, label);
if (!adapter_looks_virtual (name))
{
ptr_array_add_unique (physical, label);
}
g_free (label);
g_free (description);
g_free (friendly);
}
g_free (addresses);
selected = physical->len != 0 ? physical : fallback;
result = join_ptr_array (selected, ", ");
g_ptr_array_free (physical, TRUE);
g_ptr_array_free (fallback, TRUE);
return result;
}
char *
sysinfo_backend_get_ui (void)
{
const char *toolkit = sysinfo_detect_toolkit();
/* On Windows we don't have X11/Wayland. Keep it simple. */
if (toolkit)
{
return g_strdup_printf ("Windows / %s", toolkit);
}
#if defined(USE_GTK_FRONTEND)
return g_strdup ("Windows / GTK3");
#else
return g_strdup ("Windows");
#endif
}
+1
View File
@@ -591,6 +591,7 @@ const struct prefs vars[] =
{"text_replay", P_OFFINT (hex_text_replay), TYPE_BOOL},
{"text_search_case_match", P_OFFINT (hex_text_search_case_match), TYPE_BOOL},
{"text_search_highlight_all", P_OFFINT (hex_text_search_highlight_all), TYPE_BOOL},
{"text_search_keep_position", P_OFFINT (hex_text_search_keep_position), TYPE_BOOL},
{"text_search_follow", P_OFFINT (hex_text_search_follow), TYPE_BOOL},
{"text_search_regexp", P_OFFINT (hex_text_search_regexp), TYPE_BOOL},
{"text_show_marker", P_OFFINT (hex_text_show_marker), TYPE_BOOL},
+15 -1
View File
@@ -126,7 +126,21 @@ ctcp_handle (session *sess, char *to, char *nick, char *ip,
if (ctcp_check (sess, nick, word, word_eol, word[4] + ctcp_offset))
goto generic;
inbound_action (sess, to, nick, ip, msg + 7, FALSE, tags_data->identified, tags_data);
{
gboolean private_fromme = !serv->p_cmp (nick, serv->nick) && !is_channel (serv, to);
if (private_fromme)
{
session *target = find_dialog (serv, to);
if (target)
sess = target;
else if (serv->front_session)
sess = serv->front_session;
}
inbound_action (sess, to, nick, ip, msg + 7, private_fromme, tags_data->identified, tags_data);
}
return;
}
+1 -1
View File
@@ -1368,7 +1368,7 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[],
{
if (ignore_check (word[1], IG_PRIV))
return;
if (serv->have_echo_message && !serv->p_cmp (nick, serv->nick))
if (!serv->p_cmp (nick, serv->nick))
{
session *target_sess = find_dialog (serv, to);
-8
View File
@@ -374,14 +374,6 @@ server_inline (server *serv, char *line, gssize len)
else
line = text_convert_invalid (line, len, serv->read_converter, unicode_fallback_string, &len_utf8);
/* Any complete IRC line proves that the connection is alive. */
serv->ping_recv = time (0);
if (serv->lag_sent)
{
serv->lag_sent = 0;
fe_set_lag (serv, serv->lag);
}
fe_add_rawlog (serv, line, len_utf8, FALSE);
/* let proto-irc.c handle it */
+1
View File
@@ -217,6 +217,7 @@ struct zoitechatprefs
unsigned int hex_text_replay;
unsigned int hex_text_search_case_match;
unsigned int hex_text_search_highlight_all;
unsigned int hex_text_search_keep_position;
unsigned int hex_text_search_follow;
unsigned int hex_text_search_regexp;
unsigned int hex_text_show_marker;
+15 -2
View File
@@ -4688,7 +4688,11 @@ mg_search_toggle(session *sess)
{
gtk_widget_hide(sess->gui->shbox);
gtk_widget_grab_focus(sess->gui->input_box);
if (prefs.hex_text_search_keep_position)
g_signal_handler_block (sess->gui->shentry, sess->gui->search_changed_signal);
gtk_entry_set_text(GTK_ENTRY(sess->gui->shentry), "");
if (prefs.hex_text_search_keep_position)
g_signal_handler_unblock (sess->gui->shentry, sess->gui->search_changed_signal);
}
else
{
@@ -4702,10 +4706,19 @@ mg_search_toggle(session *sess)
}
static gboolean
search_handle_esc (GtkWidget *win, GdkEventKey *key, session *sess)
search_handle_keypress (GtkWidget *win, GdkEventKey *key, session *sess)
{
if (key->keyval == GDK_KEY_Escape)
{
mg_search_toggle(sess);
return TRUE;
}
if (((key->keyval == GDK_KEY_Page_Up || key->keyval == GDK_KEY_Page_Down) &&
!(key->state & gtk_accelerator_get_default_mod_mask ())) ||
((key->keyval == GDK_KEY_Up || key->keyval == GDK_KEY_Down) &&
(key->state & gtk_accelerator_get_default_mod_mask ()) == GDK_SHIFT_MASK))
return key_handle_key_press (sess->gui->input_box, key, sess);
return FALSE;
}
@@ -4735,7 +4748,7 @@ mg_create_search(session *sess, GtkWidget *box)
mg_apply_emoji_fallback_widget (entry);
mg_apply_entry_scroll_artifact_fix (entry);
gui->search_changed_signal = g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(search_handle_change), sess);
g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK (search_handle_esc), sess);
g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK (search_handle_keypress), sess);
g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(mg_search_handle_next), sess);
gtk_entry_set_icon_activatable (GTK_ENTRY (entry), GTK_ENTRY_ICON_SECONDARY, FALSE);
gtk_entry_set_icon_tooltip_text (GTK_ENTRY (sess->gui->shentry), GTK_ENTRY_ICON_SECONDARY, _("Search hit end or not found."));
+2 -1
View File
@@ -171,6 +171,7 @@ static const setting appearance_settings[] =
{ST_TOGGLE, N_("Colored nick names"), P_OFFINTNL(hex_text_color_nicks), N_("Give each person on IRC a different color"),0,0},
{ST_TOGGLR, N_("Indent nick names"), P_OFFINTNL(hex_text_indent), N_("Make nick names right-justified"),0,0},
{ST_TOGGLE, N_ ("Show marker line"), P_OFFINTNL (hex_text_show_marker), N_ ("Insert a red line after the last read text."), 0, 0},
{ST_TOGGLE, N_("Keep search position when closing"), P_OFFINTNL(hex_text_search_keep_position), 0, 0, 0},
{ST_HEADER, N_("Timestamps"),0,0,0},
{ST_TOGGLE, N_("Enable timestamps"), P_OFFINTNL(hex_stamp_text),0,0,1},
@@ -219,7 +220,7 @@ static const setting inputbox_settings[] =
{ST_TOGGLE, N_("Spell checking"), P_OFFINTNL(hex_gui_input_spell),0,0,1},
{ST_ENTRY, N_("Dictionaries to use:"), P_OFFSETNL(hex_text_spell_langs),0,0,sizeof prefs.hex_text_spell_langs},
#ifdef WIN32
{ST_LABEL, N_("Use language codes (as in \"%LOCALAPPDATA%\\enchant\\myspell\\dicts\").\nSeparate multiple entries with commas.")},
{ST_LABEL, N_("Use language codes (for example, \"en_US\").\nOn Windows 8 and newer, dictionaries are provided by Windows. Separate multiple entries with commas.")},
#else
{ST_LABEL, N_("Use language codes. Separate multiple entries with commas.")},
#endif
+23 -1
View File
@@ -113,6 +113,9 @@ static void (*enchant_dict_describe) (struct EnchantDict * dict, EnchantDictDesc
static void (*enchant_dict_free_suggestions) (struct EnchantDict * dict, char **suggestions);
static void (*enchant_dict_store_replacement) (struct EnchantDict * dict, const char *const mis, ssize_t mis_len, const char *const cor, ssize_t cor_len);
static char ** (*enchant_dict_suggest) (struct EnchantDict * dict, const char *const word, ssize_t len, size_t * out_n_suggs);
#ifdef G_OS_WIN32
static void (*enchant_set_prefix_dir) (const char *dir);
#endif
static gboolean have_enchant = FALSE;
struct _SexySpellEntryPriv
@@ -198,14 +201,16 @@ initialize_enchant (void)
gsize i;
const char * const libnames[] = {
#ifdef G_OS_WIN32
"libenchant-2.dll",
"libenchant-2-2.dll",
"libenchant.dll",
#endif
#ifdef G_OS_UNIX
"libenchant.so.1",
"libenchant.so.2",
"libenchant-2.so.2",
#endif
#ifdef __APPLE__
"libenchant-2.dylib",
"libenchant.dylib",
#endif
};
@@ -255,6 +260,23 @@ initialize_enchant (void)
MODULE_SYMBOL("enchant_dict_store_replacement",
enchant_dict_store_replacement, NULL)
MODULE_SYMBOL("enchant_dict_suggest", enchant_dict_suggest, NULL)
#ifdef G_OS_WIN32
if (g_module_symbol(enchant, "enchant_set_prefix_dir", &funcptr))
{
gchar *install_dir;
enchant_set_prefix_dir = funcptr;
install_dir = g_win32_get_package_installation_directory_of_module(NULL);
if (install_dir)
{
enchant_set_prefix_dir(install_dir);
g_info("Enchant prefix dir set to %s", install_dir);
g_free(install_dir);
}
}
#endif
}
static void
-2
View File
@@ -1,2 +0,0 @@
EXPORTS
init_enchant_provider
@@ -1,43 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{BF0EBC16-68AD-4CD1-864C-5B56836EBE2A}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>libenchant_win8</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="..\..\win32\zoitechat.props" />
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup>
<OutDir>$(ZoiteChatRel)lib\enchant\</OutDir>
<TargetName>libenchant_win8</TargetName>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>..\common;$(ZoiteChatLib);$(DepsRoot)\include\enchant;$(DepsRoot)\include;$(OpenSslInclude);$(Glib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<ModuleDefinitionFile>libenchant_win8.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="win8_provider.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>
@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="win8_provider.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
-340
View File
@@ -1,340 +0,0 @@
/* ZoiteChat
* Copyright (c) 2015 Patrick Griffis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "config.h"
#include <Spellcheck.h>
#include <windows.h>
#include <cstdlib>
#include <climits>
#include "typedef.h" // for ssize_t
#include <enchant-provider.h>
ENCHANT_PLUGIN_DECLARE ("win8")
static int
size_to_int (size_t value)
{
return value > static_cast<size_t>(INT_MAX) ? INT_MAX : static_cast<int>(value);
}
static char *
utf16_to_utf8 (const wchar_t * const str, bool from_bcp47)
{
if (!str)
return nullptr;
int needed = WideCharToMultiByte (CP_UTF8, 0, str, -1, nullptr, 0, nullptr, nullptr);
if (needed <= 0)
return nullptr;
char *utf8 = static_cast<char*>(std::malloc (static_cast<size_t>(needed)));
if (!utf8)
return nullptr;
if (WideCharToMultiByte (CP_UTF8, 0, str, -1, utf8, needed, nullptr, nullptr) != needed)
{
std::free (utf8);
return nullptr;
}
if (utf8 && from_bcp47)
{
char *p = utf8;
/* bcp47 tags use syntax "en-US" while the myspell versions are "en_US" */
while (*p)
{
if (*p == '-')
*p = '_';
p++;
}
}
return utf8;
}
static wchar_t *
utf8_to_utf16 (const char * const str, int len, bool to_bcp47)
{
if (!str)
return nullptr;
int needed = MultiByteToWideChar (CP_UTF8, 0, str, len, nullptr, 0);
if (needed <= 0)
return nullptr;
int alloc_len = (len == -1) ? needed : needed + 1;
wchar_t *utf16 = static_cast<wchar_t*>(std::malloc (sizeof (wchar_t) * static_cast<size_t>(alloc_len)));
if (!utf16)
return nullptr;
if (MultiByteToWideChar (CP_UTF8, 0, str, len, utf16, needed) != needed)
{
std::free (utf16);
return nullptr;
}
if (len != -1)
utf16[needed] = L'\0';
if (utf16 && to_bcp47)
{
wchar_t *p = utf16;
/* bcp47 tags use syntax "en-US" while the myspell versions are "en_US" */
while (*p)
{
if (*p == L'_')
*p = L'-';
p++;
}
}
return utf16;
}
static char **
enumstring_to_chararray (IEnumString *strings, size_t *out_len, bool from_bcp47)
{
char **chars = static_cast<char**>(std::calloc (256, sizeof (char*)));
LPOLESTR wstr = nullptr;
size_t i = 0;
if (!chars)
{
*out_len = 0;
strings->Release ();
return nullptr;
}
while (SUCCEEDED (strings->Next (1, &wstr, nullptr)) && i < 256 && wstr)
{
char *str = utf16_to_utf8 (wstr, from_bcp47);
if (str)
{
chars[i] = str;
i++;
}
CoTaskMemFree (wstr);
}
chars[i] = nullptr;
strings->Release ();
*out_len = i;
return chars;
}
static void
win8_dict_add_to_personal (EnchantDict *dict, const char *const word, size_t len)
{
auto checker = static_cast<ISpellChecker*>(dict->user_data);
wchar_t *wword = utf8_to_utf16 (word, size_to_int (len), false);
checker->Add (wword);
std::free (wword);
}
static void
win8_dict_add_to_session (EnchantDict *dict, const char *const word, size_t len)
{
auto checker = static_cast<ISpellChecker*>(dict->user_data);
wchar_t *wword = utf8_to_utf16 (word, size_to_int (len), false);
checker->Ignore (wword);
std::free (wword);
}
static int
win8_dict_check (EnchantDict *dict, const char *const word, size_t len)
{
auto checker = static_cast<ISpellChecker*>(dict->user_data);
wchar_t *wword = utf8_to_utf16 (word, size_to_int (len), false);
IEnumSpellingError *errors;
ISpellingError *error = nullptr;
HRESULT hr;
hr = checker->Check (wword, &errors);
std::free (wword);
if (FAILED (hr))
return -1;
if (errors->Next (&error) == S_OK)
{
error->Release ();
errors->Release ();
return 1;
}
else
{
errors->Release ();
return 0;
}
}
static char **
win8_dict_suggest (EnchantDict *dict, const char *const word, size_t len, size_t *out_n_suggs)
{
auto checker = static_cast<ISpellChecker*>(dict->user_data);
wchar_t *wword = utf8_to_utf16 (word, size_to_int (len), false);
IEnumString *suggestions;
HRESULT hr;
hr = checker->Suggest (wword, &suggestions);
std::free (wword);
if (FAILED (hr))
{
*out_n_suggs = 0;
return nullptr;
}
return enumstring_to_chararray (suggestions, out_n_suggs, false);
}
static EnchantDict *
win8_provider_request_dict (EnchantProvider *provider, const char *const tag)
{
auto factory = static_cast<ISpellCheckerFactory*>(provider->user_data);
ISpellChecker *checker;
EnchantDict *dict;
wchar_t *wtag = utf8_to_utf16 (tag, -1, true);
HRESULT hr;
hr = factory->CreateSpellChecker (wtag, &checker);
std::free (wtag);
if (FAILED (hr))
return nullptr;
dict = static_cast<EnchantDict*>(std::calloc (1, sizeof (EnchantDict)));
dict->suggest = win8_dict_suggest;
dict->check = win8_dict_check;
dict->add_to_personal = win8_dict_add_to_personal;
dict->add_to_exclude = win8_dict_add_to_personal;
dict->add_to_session = win8_dict_add_to_session;
dict->user_data = checker;
return dict;
}
static void
win8_provider_dispose_dict (EnchantProvider *provider, EnchantDict *dict)
{
if (dict)
{
auto checker = static_cast<ISpellChecker*>(dict->user_data);
checker->Release ();
std::free (dict);
}
}
static int
win8_provider_dictionary_exists (EnchantProvider *provider, const char *const tag)
{
auto factory = static_cast<ISpellCheckerFactory*>(provider->user_data);
wchar_t *wtag = utf8_to_utf16 (tag, -1, true);
BOOL is_supported = FALSE;
factory->IsSupported (wtag, &is_supported);
std::free (wtag);
return is_supported;
}
static char **
win8_provider_list_dicts (EnchantProvider *provider, size_t *out_n_dicts)
{
auto factory = static_cast<ISpellCheckerFactory*>(provider->user_data);
IEnumString *dicts;
if (FAILED(factory->get_SupportedLanguages (&dicts)))
{
*out_n_dicts = 0;
return nullptr;
}
return enumstring_to_chararray (dicts, out_n_dicts, true);
}
static void
win8_provider_free_string_list (EnchantProvider *provider, char **str_list)
{
if (!str_list)
return;
for (size_t i = 0; str_list[i]; ++i)
std::free (str_list[i]);
std::free (str_list);
}
static void
win8_provider_dispose (EnchantProvider *provider)
{
if (provider)
{
auto factory = static_cast<ISpellCheckerFactory*>(provider->user_data);
factory->Release();
std::free (provider);
}
}
static const char *
win8_provider_identify (EnchantProvider *provider)
{
return "win8";
}
static const char *
win8_provider_describe (EnchantProvider *provider)
{
return "Windows 8 SpellCheck Provider";
}
extern "C"
{
EnchantProvider *
init_enchant_provider (void)
{
EnchantProvider *provider;
ISpellCheckerFactory *factory;
if (FAILED (CoCreateInstance (__uuidof(SpellCheckerFactory), nullptr,
CLSCTX_INPROC_SERVER, IID_PPV_ARGS (&factory))))
return nullptr;
provider = static_cast<EnchantProvider*>(std::calloc (1, sizeof (EnchantProvider)));
provider->dispose = win8_provider_dispose;
provider->request_dict = win8_provider_request_dict;
provider->dispose_dict = win8_provider_dispose_dict;
provider->dictionary_exists = win8_provider_dictionary_exists;
provider->identify = win8_provider_identify;
provider->describe = win8_provider_describe;
provider->list_dicts = win8_provider_list_dicts;
provider->free_string_list = win8_provider_free_string_list;
provider->user_data = factory;
return provider;
}
}
+2 -2
View File
@@ -73,7 +73,7 @@
<LuaShare Include="$(LuaShareDir)\**\*.lua" Condition="'$(LuaEnabled)'=='true' and '$(LuaShareDir)'!=''" />
<Typelib Include="$(DepsRoot)\lib\girepository-1.0\*.typelib" Condition="'$(LuaEnabled)'=='true' and '$(LuaLibDir)'!=''" />
<EnchantProviders Include="$(DepsRoot)\lib\enchant\*.dll" />
<EnchantProviders Include="$(DepsRoot)\lib\enchant-2\*.dll" />
<Gtk3Immodules Include="$(DepsRoot)\lib\gtk-3.0\3.0.0\immodules\**\*" />
<Gtk3PrintBackends Include="$(DepsRoot)\lib\gtk-3.0\3.0.0\printbackends\**\*" />
@@ -99,7 +99,7 @@
<Copy SourceFiles="@(Share)" DestinationFiles="@(Share->'$(ZoiteChatRel)\share\%(RecursiveDir)%(Filename)%(Extension)')" />
<Copy SourceFiles="..\..\COPYING" DestinationFolder="$(ZoiteChatRel)\share\doc\zoitechat" />
<Copy SourceFiles="$(WinSparklePath)\COPYING" DestinationFolder="$(ZoiteChatRel)\share\doc\WinSparkle" />
<Copy SourceFiles="@(EnchantProviders)" DestinationFolder="$(ZoiteChatRel)\lib\enchant" />
<Copy SourceFiles="@(EnchantProviders)" DestinationFolder="$(ZoiteChatRel)\lib\enchant-2" />
<Copy SourceFiles="@(Locale)" DestinationFiles="@(Locale->'$(ZoiteChatRel)\share\locale\%(RecursiveDir)%(Filename)%(Extension)')" />
<Copy SourceFiles="@(MSWindowsTheme)" DestinationFiles="@(MSWindowsTheme->'$(ZoiteChatRel)\share\themes\MS-Windows\%(RecursiveDir)%(Filename)%(Extension)')" />
<Copy SourceFiles="@(HicolorIcons)" DestinationFiles="@(HicolorIcons->'$(ZoiteChatRel)\share\icons\hicolor\%(RecursiveDir)%(Filename)%(Extension)')" />
+6 -5
View File
@@ -49,7 +49,7 @@ Name: "icons"; Description: "Create Shortcuts"; Types: custom; Flags: disablenou
Name: "icons\desktopicon"; Description: "Create Desktop Shortcut"; Types: custom; Flags: disablenouninstallwarning
Name: "icons\quicklaunchicon"; Description: "Create Quick Launch Shortcut"; Types: custom; Flags: disablenouninstallwarning
Name: "translations"; Description: "Translations"; Types: normal custom; Flags: disablenouninstallwarning
Name: "spell"; Description: "Spelling Dictionaries"; Types: custom; Flags: disablenouninstallwarning
Name: "spell"; Description: "Spelling Dictionaries (Windows 7 fallback)"; Types: custom; Flags: disablenouninstallwarning
Name: "plugins"; Description: "Plugins"; Types: custom; Flags: disablenouninstallwarning
Name: "plugins\checksum"; Description: "Checksum"; Types: custom; Flags: disablenouninstallwarning
Name: "plugins\exec"; Description: "Exec"; Types: custom; Flags: disablenouninstallwarning
@@ -150,7 +150,7 @@ Source: "tiff.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "plugins\hcnotifications-winrt.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: libs
Source: "lib\enchant\*"; DestDir: "{app}\lib\enchant"; Flags: ignoreversion; Components: libs
Source: "lib\enchant-2\*"; DestDir: "{app}\lib\enchant-2"; Flags: ignoreversion; Components: libs
Source: "girepository-2.0-0.dll"; DestDir: "{app}"; Flags: ignoreversion skipifsourcedoesntexist; Components: langs\lua
Source: "lua51.dll"; DestDir: "{app}"; Flags: ignoreversion skipifsourcedoesntexist; Components: langs\lua
@@ -322,11 +322,12 @@ var
begin
GetWindowsVersionEx(Version);
// Windows 8 or greater has built in spell check.
if Version.NTPlatform and (Version.Major > 6) or ((Version.Major = 6) and (Version.Minor > 1)) then
// Enchant's WinSpell provider uses the built-in spell checker on Windows 8+.
// Downloaded dictionaries for other providers are retained only as a Windows 7 fallback.
if Version.NTPlatform and ((Version.Major > 6) or ((Version.Major = 6) and (Version.Minor > 1))) then
Result := True
else
Result := DirExists(ExpandConstant('{localappdata}') + '\enchant\myspell');
Result := DirExists(ExpandConstant('{localappdata}') + '\enchant-2\myspell');
end;
// Python >= 3.9 dropped support for Windows versions before 10;
-9
View File
@@ -61,7 +61,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "installer", "installer\inst
ProjectSection(ProjectDependencies) = postProject
{C2321A03-0BA7-45B3-8740-ABD82B36B0BF} = {C2321A03-0BA7-45B3-8740-ABD82B36B0BF}
{D58924D2-1492-4AC3-B058-7A214424EEDC} = {D58924D2-1492-4AC3-B058-7A214424EEDC}
{BF0EBC16-68AD-4CD1-864C-5B56836EBE2A} = {BF0EBC16-68AD-4CD1-864C-5B56836EBE2A}
{17E4BE39-76F7-4A06-AD21-EFD0C5091F76} = {17E4BE39-76F7-4A06-AD21-EFD0C5091F76}
{4C0F3940-2EEE-4646-82F7-6CE75B9A72F4} = {4C0F3940-2EEE-4646-82F7-6CE75B9A72F4}
{B10A2C41-344C-43E0-A32D-B9587C198D8B} = {B10A2C41-344C-43E0-A32D-B9587C198D8B}
@@ -95,11 +94,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "notifications-winrt", "..\s
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "external", "external", "{021EC1D0-FF67-4700-9AB2-EAABF1159C09}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libenchant_win8", "..\src\libenchant_win8\libenchant_win8.vcxproj", "{BF0EBC16-68AD-4CD1-864C-5B56836EBE2A}"
ProjectSection(ProjectDependencies) = postProject
{87554B59-006C-4D94-9714-897B27067BA3} = {87554B59-006C-4D94-9714-897B27067BA3}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lua", "..\plugins\lua\lua.vcxproj", "{4C0F3940-2EEE-4646-82F7-6CE75B9A72F4}"
EndProject
Global
@@ -137,8 +131,6 @@ Global
{D58924D2-1492-4AC3-B058-7A214424EEDC}.Release|x64.Build.0 = Release|x64
{C53145CC-D021-40C9-B97C-0249AB9A43C9}.Release|x64.ActiveCfg = Release|x64
{C53145CC-D021-40C9-B97C-0249AB9A43C9}.Release|x64.Build.0 = Release|x64
{BF0EBC16-68AD-4CD1-864C-5B56836EBE2A}.Release|x64.ActiveCfg = Release|x64
{BF0EBC16-68AD-4CD1-864C-5B56836EBE2A}.Release|x64.Build.0 = Release|x64
{4C0F3940-2EEE-4646-82F7-6CE75B9A72F4}.Release|x64.ActiveCfg = Release|x64
{4C0F3940-2EEE-4646-82F7-6CE75B9A72F4}.Release|x64.Build.0 = Release|x64
EndGlobalSection
@@ -161,7 +153,6 @@ Global
{C2321A03-0BA7-45B3-8740-ABD82B36B0BF} = {D237DA6B-BD5F-46C0-8BEA-50E9A1340240}
{D58924D2-1492-4AC3-B058-7A214424EEDC} = {D237DA6B-BD5F-46C0-8BEA-50E9A1340240}
{C53145CC-D021-40C9-B97C-0249AB9A43C9} = {561126F4-FA18-45FC-A2BF-8F858F161D6D}
{BF0EBC16-68AD-4CD1-864C-5B56836EBE2A} = {021EC1D0-FF67-4700-9AB2-EAABF1159C09}
{4C0F3940-2EEE-4646-82F7-6CE75B9A72F4} = {D237DA6B-BD5F-46C0-8BEA-50E9A1340240}
EndGlobalSection
EndGlobal