• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

NeonGE / geEngineSDK / ac7673f3-08dc-4326-87b7-3c14ad7938bf

05 Mar 2026 04:29PM UTC coverage: 57.84% (-0.02%) from 57.862%
ac7673f3-08dc-4326-87b7-3c14ad7938bf

push

circleci

NeonGE
Refactor input mapping, improve exception safety & tasks

- Replaced SFML-to-ImGui key mapping switch with static map for clarity.
- Updated input event lambdas to avoid capturing `this`.
- Marked `swap` methods as `_NOEXCEPT` in Matrix4, Path, Vector2.
- Added assertions and removed redundant checks in frame allocator.
- Refactored platform termination logic for clarity and portability.
- Modernized task scheduler waits using condition variable predicates.
- Updated debug.css font stack for better cross-platform support.

6 of 11 new or added lines in 4 files covered. (54.55%)

8 existing lines in 3 files now uncovered.

5603 of 9687 relevant lines covered (57.84%)

9107.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

7.27
/sdk/geUtilities/src/gePlatformUtility.cpp
1
/*****************************************************************************/
2
/**
3
 * @file    gePlatformUtility.cpp
4
 * @author  Samuel Prince (samuel.prince.quezada@gmail.com)
5
 * @date    2025/02/24
6
 * @brief   Platform Utilities implementation for multi-platform
7
 *
8
 * Implementation of multi-platform versions of some older system specific
9
 * implementations that were previously only available on Windows.
10
 *
11
 * @bug            No known bugs.
12
 *
13
 * @update  2026/01/22 Samuel Prince - Added POSIX implementation.
14
 * @update  2024/06/12 Samuel Prince - Added terminate implementation.
15
 * @update  2024/06/24 Samuel Prince - Added necessary includes for PS4/PS5.
16
 */
17
/*****************************************************************************/
18

19
/*****************************************************************************/
20
/**
21
 * Includes
22
 */
23
 /*****************************************************************************/
24
#include "gePrerequisitesUtilities.h"
25
#include "gePlatformUtility.h"
26
#include "geUUID.h"
27
#include "geUnicode.h"
28
#include "geDebug.h"
29
#include "geColor.h"
30

31
#if USING(GE_PLATFORM_WINDOWS) || USING(GE_PLATFORM_XBOX)
32
# include "win32/geMinWindows.h"
33
# include <shellapi.h>
34
# include <iphlpapi.h>
35
# include <rpc.h>
36
# include <intrin.h>
37
# include <VersionHelpers.h>
38
#endif
39

40
#if USING(GE_PLATFORM_PS4) || USING(GE_PLATFORM_PS5)
41
# include <kernel.h>
42
#endif
43

44
//POSIX family (Linux/mac/iOS/Android)
45
#if USING(GE_PLATFORM_LINUX) || USING(GE_PLATFORM_OSX) || \
46
    USING(GE_PLATFORM_ANDROID) || USING(GE_PLATFORM_IOS)
47

48
# include <unistd.h>
49
# include <sys/utsname.h>
50

51
# include <ifaddrs.h>
52
# include <net/if.h>
53

54
# if USING(GE_PLATFORM_OSX) || USING(GE_PLATFORM_IOS)
55
#   include <sys/sysctl.h>
56
#   include <net/if_dl.h>
57
#   include <mach-o/dyld.h>
58

59
    //For keyCodeToUnicode on macOS (no iOS)
60
#   if USING(GE_PLATFORM_OSX)
61
#     include <Carbon/Carbon.h>
62
#   endif
63
# else
64
    //Linux/Android
65
#   include <netpacket/packet.h>
66
#   if USING(GE_PLATFORM_LINUX)
67
#     include <sys/sysinfo.h>
68
#   endif
69
# endif
70

71
# if __has_include(<uuid/uuid.h>)
72
#   include <uuid/uuid.h>
73
#   define GE_HAS_LIBUUID 1
74
# else
75
#   define GE_HAS_LIBUUID 0
76
# endif
77

78
  //Optional ICU for proper Unicode case conversion (recommended)
79
# if __has_include(<unicode/unistr.h>) && __has_include(<unicode/locid.h>)
80
#   include <unicode/unistr.h>
81
#   include <unicode/locid.h>
82
#   define GE_HAS_ICU 1
83
# else
84
#   define GE_HAS_ICU 0
85
# endif
86

87
  //x86 cpuid (Linux/mac) - optional, only for x86/x64 clang/gcc
88
# if (defined(__i386__) || defined(__x86_64__)) && \
89
     (defined(__GNUC__) || defined(__clang__))
90
#   include <cpuid.h>
91
#   define GE_HAS_X86_CPUID 1
92
# else
93
#   define GE_HAS_X86_CPUID 0
94
# endif
95

96
#endif
97

98
namespace geEngineSDK {
99
  GPUInfo PlatformUtility::s_gpuInfo;
100

101
  GE_NORETURN void
102
  PlatformUtility::terminate(bool force) {
×
103
#if USING(GE_PLATFORM_WINDOWS) || USING(GE_PLATFORM_XBOX)
104
    if (!force) {
105
      PostQuitMessage(0);
106
    }
107
    else{
108
      TerminateProcess(GetCurrentProcess(), 0);
109
    }
110
#else
111
    GE_UNREFERENCED_PARAMETER(force);
NEW
112
    _exit(0);
×
113
#endif
114
  }
115

116
  SystemInfo
117
  PlatformUtility::getSystemInfo() {
×
118
    SystemInfo output;
×
119

120
#if USING(GE_PLATFORM_WINDOWS) || USING(GE_PLATFORM_XBOX)
121
    int32 CPUInfo[4] = { -1 };
122

123
    __cpuid(CPUInfo, 0);
124
    output.cpuManufacturer = String(12, ' ');
125

126
#if USING(GE_CPP17_OR_LATER)
127
    auto pCPUManuf = output.cpuManufacturer.data();
128
#else
129
    auto pCPUManuf = &output.cpuManufacturer[0];
130
#endif
131
    memcpy(cast::re<char*>(pCPUManuf) + 0, &CPUInfo[1], 4);
132
    memcpy(cast::re<char*>(pCPUManuf) + 4, &CPUInfo[3], 4);
133
    memcpy(cast::re<char*>(pCPUManuf) + 8, &CPUInfo[2], 4);
134

135
    String brandString;
136
    brandString.resize(48);
137

138
    __cpuid(CPUInfo, 0x80000000);
139
    uint32 numExtensionIds = static_cast<uint32>(CPUInfo[0]);
140
    for (uint32 i = 0x80000000; i <= numExtensionIds; ++i) {
141
      __cpuid(CPUInfo, i);
142

143
      if (0x80000002 == i) {
144
        memcpy(&brandString[0], CPUInfo, sizeof(CPUInfo));
145
      }
146
      else if (0x80000003 == i) {
147
        memcpy(&brandString[16], CPUInfo, sizeof(CPUInfo));
148
      }
149
      else if (0x80000004 == i) {
150
        memcpy(&brandString[32], CPUInfo, sizeof(CPUInfo));
151
      }
152
    }
153
    output.cpuModel = brandString;
154

155
    SYSTEM_INFO sysInfo;
156
    GetSystemInfo(&sysInfo);
157
    output.cpuNumCores = static_cast<uint32>(sysInfo.dwNumberOfProcessors);
158

159
    //CPU clock (Windows: registry)
160
    HKEY hKey;
161
    LSTATUS status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
162
                                  R"(HARDWARE\DESCRIPTION\System\CentralProcessor\0)",
163
                                  0,
164
                                  KEY_READ,
165
                                  &hKey);
166
    if (ERROR_SUCCESS == status) {
167
      DWORD mhz;
168
      DWORD bufferSize = 4;
169
      RegQueryValueEx(hKey,
170
                      "~MHz",
171
                      nullptr,
172
                      nullptr,
173
                      reinterpret_cast<LPBYTE>(&mhz),
174
                      &bufferSize);
175
      output.cpuClockSpeedMhz = static_cast<uint32>(mhz);
176
    }
177
    else {
178
      output.cpuClockSpeedMhz = 0;
179
    }
180

181
    MEMORYSTATUSEX statex;
182
    statex.dwLength = sizeof(statex);
183
    GlobalMemoryStatusEx(&statex);
184
    output.memoryAmountMb = static_cast<uint32>(statex.ullTotalPhys / (1024 * 1024));
185

186
#if USING(GE_ARCHITECTURE_x86_64)
187
    output.osIs64Bit = true;
188
#else
189
    HANDLE process = GetCurrentProcess();
190
    BOOL is64Bit = false;
191
    IsWow64Process(process, reinterpret_cast<PBOOL>(&is64Bit));
192
    output.osIs64Bit = is64Bit > 0;
193
#endif
194
    output.osName = "Windows";
195
    output.gpuInfo = s_gpuInfo;
196
    return output;
197

198
#elif USING(GE_PLATFORM_LINUX) || USING(GE_PLATFORM_OSX) || \
199
      USING(GE_PLATFORM_ANDROID) || USING(GE_PLATFORM_IOS)
200

201
    //Cores
202
    uint32 cores = cast::st<uint32>(GE_THREAD_HARDWARE_CONCURRENCY);
×
203
    output.cpuNumCores = cores > 0 ? cores : 1;
×
204

205
    //CPU vendor/model (x86 only)
206
#if GE_HAS_X86_CPUID
207
    {
208
      String brand, vendor;
×
209
      brand.clear(); vendor.clear();
×
210

211
      uint32 eax = 0, ebx = 0, ecx = 0, edx = 0;
×
212
      if (__get_cpuid(0, &eax, &ebx, &ecx, &edx)) {
×
213
        char v[13] = {};
×
214
        std::memcpy(v + 0, &ebx, 4);
×
215
        std::memcpy(v + 4, &edx, 4);
×
216
        std::memcpy(v + 8, &ecx, 4);
×
217
        vendor = String(v);
×
218
      }
219

220
      uint32 maxExt = 0;
×
221
      __get_cpuid(0x80000000, &maxExt, &ebx, &ecx, &edx);
×
222
      if (maxExt >= 0x80000004) {
×
223
        char b[49] = {};
×
224
        uint32 regs[4];
225

226
        __get_cpuid(0x80000002, &regs[0], &regs[1], &regs[2], &regs[3]);
×
227
        std::memcpy(b + 0, regs, sizeof(regs));
×
228
        __get_cpuid(0x80000003, &regs[0], &regs[1], &regs[2], &regs[3]);
×
229
        std::memcpy(b + 16, regs, sizeof(regs));
×
230
        __get_cpuid(0x80000004, &regs[0], &regs[1], &regs[2], &regs[3]);
×
231
        std::memcpy(b + 32, regs, sizeof(regs));
×
232

233
        brand = String(b);
×
234
      }
235

236
      output.cpuManufacturer = vendor.empty() ? "Unknown" : vendor;
×
237
      output.cpuModel = brand.empty() ? "Unknown" : brand;
×
238
    }
×
239
#else
240
    output.cpuManufacturer = "Unknown";
241
    output.cpuModel = "Unknown";
242
#endif
243

244
    // CPU clock (best effort)
245
    output.cpuClockSpeedMhz = 0;
×
246
#if USING(GE_PLATFORM_OSX) || USING(GE_PLATFORM_IOS)
247
    {
248
      uint64_t freqHz = 0;
249
      size_t sz = sizeof(freqHz);
250
      if (0 == sysctlbyname("hw.cpufrequency", &freqHz, &sz, nullptr, 0) && freqHz > 0) {
251
        output.cpuClockSpeedMhz = (uint32)(freqHz / 1000000ULL);
252
      }
253
    }
254
#elif USING(GE_PLATFORM_LINUX)
255
    {
256
      FILE* f = fopen("/proc/cpuinfo", "r");
×
257
      if (f) {
×
258
        char line[512];
259
        while (fgets(line, sizeof(line), f)) {
×
260
          if (0 == std::strncmp(line, "cpu MHz", 7)) {
×
261
            const char* colon = std::strchr(line, ':');
×
262
            if (colon) {
×
263
              double mhz = std::atof(colon + 1);
×
264
              if (mhz > 0.0) {
×
265
                output.cpuClockSpeedMhz = static_cast<uint32>(mhz);
×
266
                break;
×
267
              }
268
            }
269
          }
270
        }
271
        fclose(f);
×
272
      }
273
    }
274
#endif
275

276
    // Memory (best effort)
277
    output.memoryAmountMb = 0;
×
278
#if USING(GE_PLATFORM_OSX) || USING(GE_PLATFORM_IOS)
279
    {
280
      uint64_t memBytes = 0;
281
      size_t memSize = sizeof(memBytes);
282
      if (0 == sysctlbyname("hw.memsize", &memBytes, &memSize, nullptr, 0) && memBytes > 0) {
283
        output.memoryAmountMb = static_cast<uint32>(memBytes / (1024ULL * 1024ULL));
284
      }
285
    }
286
#elif USING(GE_PLATFORM_LINUX)
287
    {
288
      struct sysinfo info {};
×
289
      if (0 == sysinfo(&info)) {
×
290
        uint64_t totalBytes = uint64_t(info.totalram) * uint64_t(info.mem_unit);
×
291
        output.memoryAmountMb = static_cast<uint32>(totalBytes / (1024ULL * 1024ULL));
×
292
      }
293
    }
294
#endif
295

296
    // 64-bit
297
#if USING(GE_ARCHITECTURE_x86_64) || USING(GE_ARCHITECTURE_ARM64)
298
    output.osIs64Bit = true;
×
299
#else
300
    output.osIs64Bit = (sizeof(void*) == 8);
301
#endif
302

303
    // OS name
304
    {
305
      struct utsname u {};
×
306
      if (0 == uname(&u)) {
×
307
#if USING(GE_PLATFORM_OSX)
308
        output.osName = String("macOS ") + u.sysname + " " + u.release;
309
#elif USING(GE_PLATFORM_IOS)
310
        output.osName = String("iOS ") + u.sysname + " " + u.release;
311
#elif USING(GE_PLATFORM_ANDROID)
312
        output.osName = String("Android ") + u.sysname + " " + u.release;
313
#else
314
        output.osName = String("Linux ") + u.sysname + " " + u.release;
×
315
#endif
316
      }
317
      else {
318
        output.osName = "Unknown";
×
319
      }
320
    }
321

322
    output.gpuInfo = s_gpuInfo;
×
323
    return output;
×
324

325
#else
326
    // Other platforms: return minimal info
327
    output.cpuManufacturer = "Unknown";
328
    output.cpuModel = "Unknown";
329
    output.cpuNumCores = 1;
330
    output.cpuClockSpeedMhz = 0;
331
    output.memoryAmountMb = 0;
332
    output.osIs64Bit = (sizeof(void*) == 8);
333
    output.osName = "Unknown";
334
    output.gpuInfo = s_gpuInfo;
335
    return output;
336
#endif
337
  }
×
338

339
  //===========================================================================
340
  // keyCodeToUnicode (Win + macOS; Linux generic fallback)
341
  //===========================================================================
342
  WString
343
  PlatformUtility::keyCodeToUnicode(uint32 keyCode) {
×
344
#if USING(GE_PLATFORM_WINDOWS) || USING(GE_PLATFORM_XBOX)
345
    static HKL keyboardLayout = GetKeyboardLayout(0);
346
    static uint8 keyboarState[256];
347

348
    if (FALSE == GetKeyboardState(keyboarState)) {
349
      return WString();
350
    }
351

352
    uint32 virtualKey = MapVirtualKeyExW(keyCode, 1, keyboardLayout);
353

354
    UNICHAR output[2];
355
    int32 count = ToUnicodeEx(virtualKey,
356
                              keyCode,
357
                              keyboarState,
358
                              output,
359
                              2,
360
                              0,
361
                              keyboardLayout);
362
    if (0 < count) {
363
      return WString(output, count);
364
    }
365

366
    return StringUtil::WBLANK;
367

368
#elif USING(GE_PLATFORM_OSX)
369
    //macOS: must be a "virtual keycode" like kVK_* 
370
    TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardLayoutInputSource();
371
    if (!currentKeyboard) {
372
      return WString();
373
    }
374

375
    CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard,
376
                             kTISPropertyUnicodeKeyLayoutData);
377

378
    if (!layoutData) {
379
      CFRelease(currentKeyboard);
380
      return WString();
381
    }
382

383
    const UCKeyboardLayout* keyboardLayout =
384
      (const UCKeyboardLayout*)CFDataGetBytePtr(layoutData);
385

386
    UInt32 deadKeyState = 0;
387
    UniCharCount maxLen = 4;
388
    UniCharCount outLen = 0;
389
    UniChar chars[4] = {};
390

391
    UInt32 modifiers = 0;
392
    OSStatus status = UCKeyTranslate(keyboardLayout,
393
                                     (UInt16)keyCode,
394
                                     kUCKeyActionDown,
395
                                     (modifiers >> 8) & 0xFF,
396
                                     LMGetKbdType(),
397
                                     kUCKeyTranslateNoDeadKeysBit,
398
                                     &deadKeyState,
399
                                     maxLen,
400
                                     &outLen,
401
                                     chars);
402

403
    CFRelease(currentKeyboard);
404

405
    if (status == noErr && outLen > 0) {
406
      return WString((wchar_t*)chars, (size_t)outLen);
407
    }
408
    return WString();
409

410
#else
411
    (void)keyCode;
412
    return WString(); //Linux/iOS/Android: requires per-desktop-environment implementation
×
413
#endif
414
  }
415

416
  //===========================================================================
417
  // getMACAddress (Win + Linux/mac)
418
  //===========================================================================
419
  bool
420
  PlatformUtility::getMACAddress(MACAddress& address) {
×
421
#if USING(GE_PLATFORM_WINDOWS) || USING(GE_PLATFORM_XBOX)
422
    memset(&address, 0, sizeof(address));
423

424
    PIP_ADAPTER_INFO adapterInfo = ge_alloc<IP_ADAPTER_INFO>();
425
    ULONG len = sizeof(IP_ADAPTER_INFO);
426
    DWORD rc = GetAdaptersInfo(adapterInfo, &len);
427

428
    if (ERROR_BUFFER_OVERFLOW == rc) {
429
      ge_free(adapterInfo);
430
      adapterInfo = reinterpret_cast<IP_ADAPTER_INFO*>(ge_alloc(len));
431
    }
432
    else if (ERROR_SUCCESS != rc) {
433
      ge_free(adapterInfo);
434
      return false;
435
    }
436

437
    if (NO_ERROR == GetAdaptersInfo(adapterInfo, &len)) {
438
      PIP_ADAPTER_INFO curAdapter = adapterInfo;
439
      while (curAdapter) {
440
        if (MIB_IF_TYPE_ETHERNET == curAdapter->Type &&
441
          sizeof(address) == curAdapter->AddressLength) {
442
          memcpy(&address, curAdapter->Address, curAdapter->AddressLength);
443
          ge_free(adapterInfo);
444
          return true;
445
        }
446
        curAdapter = curAdapter->Next;
447
      }
448
    }
449

450
    ge_free(adapterInfo);
451
    return false;
452

453
#elif USING(GE_PLATFORM_LINUX) || USING(GE_PLATFORM_OSX) || \
454
      USING(GE_PLATFORM_ANDROID) || USING(GE_PLATFORM_IOS)
455

456
    std::memset(&address, 0, sizeof(address));
×
457

458
    struct ifaddrs* ifa = nullptr;
×
459
    if (0 != getifaddrs(&ifa) || !ifa) {
×
460
      return false;
×
461
    }
462

463
    bool found = false;
×
464
    for (struct ifaddrs* it = ifa; it != nullptr; it = it->ifa_next) {
×
465
      if (!it->ifa_addr) continue;
×
466
      if ((it->ifa_flags & IFF_LOOPBACK) != 0) continue;
×
467
      if ((it->ifa_flags & IFF_UP) == 0) continue;
×
468

469
#if USING(GE_PLATFORM_OSX) || USING(GE_PLATFORM_IOS)
470
      if (it->ifa_addr->sa_family == AF_LINK) {
471
        const struct sockaddr_dl* sdl = (const struct sockaddr_dl*)it->ifa_addr;
472
        if (sdl->sdl_alen == sizeof(MACAddress)) {
473
          const unsigned char* mac = (const unsigned char*)LLADDR(sdl);
474
          std::memcpy(&address, mac, sizeof(MACAddress));
475
          found = true;
476
          break;
477
        }
478
      }
479
#else
480
      if (it->ifa_addr->sa_family == AF_PACKET) {
×
481
        const auto* s = reinterpret_cast<const struct sockaddr_ll*>(it->ifa_addr);
×
482
        if (s->sll_halen == sizeof(MACAddress)) {
×
483
          std::memcpy(&address, s->sll_addr, sizeof(MACAddress));
×
484
          found = true;
×
485
          break;
×
486
        }
487
      }
488
#endif
489
    }
490

491
    freeifaddrs(ifa);
×
492
    return found;
×
493

494
#else
495
    std::memset(&address, 0, sizeof(address));
496
    return false;
497
#endif
498
  }
499

500
  //===========================================================================
501
  // generateUUID (Win + Linux/mac)
502
  //===========================================================================
503
  UUID
504
  PlatformUtility::generateUUID() {
2✔
505
#if USING(GE_PLATFORM_WINDOWS) || USING(GE_PLATFORM_XBOX)
506
    ::UUID uuid;
507
    if (RPC_S_OK != UuidCreate(&uuid)) {
508
      GE_LOG(kError, Generic, "Error creating UUID");
509
    }
510

511
    uint32 data1 = uuid.Data1;
512
    uint32 data2 = uuid.Data2 | (uuid.Data3 << 16);
513
    uint32 data3 = uuid.Data3 | (uuid.Data4[0] << 16) | (uuid.Data4[1] << 24);
514
    uint32 data4 = uuid.Data4[2] |
515
                  (uuid.Data4[3] << 8) |
516
                  (uuid.Data4[4] << 16) |
517
                  (uuid.Data4[5] << 24);
518

519
    return UUID(data1, data2, data3, data4);
520

521
#elif USING(GE_PLATFORM_LINUX) || USING(GE_PLATFORM_OSX) || \
522
      USING(GE_PLATFORM_ANDROID) || USING(GE_PLATFORM_IOS)
523

524
#if GE_HAS_LIBUUID
525
    uuid_t u;
526
    uuid_generate(u);
2✔
527

528
    uint32 data1 = 0, data2 = 0, data3 = 0, data4 = 0;
2✔
529
    std::memcpy(&data1, &u[0], 4);
2✔
530
    std::memcpy(&data2, &u[4], 4);
2✔
531
    std::memcpy(&data3, &u[8], 4);
2✔
532
    std::memcpy(&data4, &u[12], 4);
2✔
533
    return UUID(data1, data2, data3, data4);
2✔
534
#else
535
    //fallback: no libuuid
536
    uint32 data1 = static_cast<uint32>(std::rand());
537
    uint32 data2 = static_cast<uint32>(std::rand());
538
    uint32 data3 = static_cast<uint32>(std::rand());
539
    uint32 data4 = static_cast<uint32>(std::rand());
540
    return UUID(data1, data2, data3, data4);
541
#endif
542

543
#else
544
    return UUID();
545
#endif
546
  }
547

548
  //===========================================================================
549
  // convertCaseUTF8 (Win + POSIX)
550
  //===========================================================================
551
  String
552
  PlatformUtility::convertCaseUTF8(const String& input, bool toUpper) {
×
553
#if USING(GE_PLATFORM_WINDOWS) || USING(GE_PLATFORM_XBOX)
554
    if (input.empty()) {
555
      return "";
556
    }
557

558
    WString wideString = UTF8::toWide(input);
559

560
    DWORD flags = LCMAP_LINGUISTIC_CASING;
561
    flags |= toUpper ? LCMAP_UPPERCASE : LCMAP_LOWERCASE;
562

563
    uint32 reqNumChars = LCMapStringEx(LOCALE_NAME_USER_DEFAULT,
564
                                       flags,
565
                                       wideString.data(),
566
                                       static_cast<int>(wideString.length()),
567
                                       nullptr,
568
                                       0,
569
                                       nullptr,
570
                                       nullptr,
571
                                       0);
572

573
    WString outputWideString(reqNumChars, ' ');
574

575
    LCMapStringEx(LOCALE_NAME_USER_DEFAULT,
576
                  flags,
577
                  wideString.data(),
578
                  static_cast<int>(wideString.length()),
579
                  &outputWideString[0],
580
                  static_cast<int>(outputWideString.length()),
581
                  nullptr,
582
                  nullptr,
583
                  0);
584

585
    return UTF8::fromWide(outputWideString);
586

587
#elif USING(GE_PLATFORM_LINUX) || USING(GE_PLATFORM_OSX) || \
588
      USING(GE_PLATFORM_ANDROID) || USING(GE_PLATFORM_IOS)
589

590
    if (input.empty()) return "";
×
591

592
#if GE_HAS_ICU
593
    icu::UnicodeString u = icu::UnicodeString::fromUTF8(input.c_str());
×
594
    if (toUpper) u.toUpper();
×
595
    else         u.toLower();
×
596

597
    String out;
×
598
    u.toUTF8String(out);
×
599
    return out;
×
600
#else
601
    //Simple fallback (not proper Unicode handling)
602
    WString wide = UTF8::toWide(input);
603
    if (wide.empty()) return "";
604

605
    for (auto& ch : wide) {
606
      ch = (wchar_t)(toUpper ? std::towupper(ch) : std::towlower(ch));
607
    }
608
    return UTF8::fromWide(wide);
609
#endif
610

611
#else
612
    return input;
613
#endif
614
  }
×
615

616
  //===========================================================================
617
  // getExecutableName (Win + POSIX)
618
  //===========================================================================
619
  String
620
  PlatformUtility::getExecutableName() {
×
621
    String fullPath;
×
622

623
#if USING(GE_PLATFORM_WINDOWS)
624
    char buffer[MAX_PATH];
625
    DWORD len = GetModuleFileNameA(nullptr, buffer, MAX_PATH);
626
    if (len == 0 || len == MAX_PATH) {
627
      return {};
628
    }
629

630
    fullPath = buffer;
631

632
#elif USING(GE_PLATFORM_LINUX)
633
    char buffer[PATH_MAX];
634
    ssize_t len = readlink("/proc/self/exe", buffer, sizeof(buffer) - 1);
×
635
    if (len == -1) {
×
636
      return {};
×
637
    }
638

639
    buffer[len] = '\0';
×
640
    fullPath = buffer;
×
641

642
#elif USING(GE_PLATFORM_OSX)
643
    char buffer[PATH_MAX];
644
    uint32_t size = sizeof(buffer);
645

646
    if (_NSGetExecutablePath(buffer, &size) != 0) {
647
      return {}; // buffer too small
648
    }
649

650
    fullPath = buffer;
651

652
#else
653
    return {};
654
#endif
655

656
    //We extract only the name
657
    SIZE_T slash = fullPath.find_last_of("/\\");
×
658
    if (slash == String::npos) {
×
659
      return fullPath;
×
660
    }
661

662
    return fullPath.substr(slash + 1);
×
663
  }
×
664

665
  //===========================================================================
666
  // open (Win + POSIX)
667
  //===========================================================================
668
  void
669
  PlatformUtility::open(const Path& path) {
×
670
#if USING(GE_PLATFORM_WINDOWS) || USING(GE_PLATFORM_XBOX)
671
    ShellExecute(nullptr,
672
                 "open",
673
                 path.toString().c_str(),
674
                 nullptr,
675
                 nullptr,
676
                 SW_SHOWNORMAL);
677

678
#elif USING(GE_PLATFORM_OSX)
679
    const String s = path.toString();
680
    pid_t pid = fork();
681
    if (pid == 0) {
682
      execlp("open", "open", s.c_str(), nullptr);
683
      _exit(127);
684
    }
685

686
#elif USING(GE_PLATFORM_LINUX)
687
    const String s = path.toString();
×
688
    pid_t pid = fork();
×
689
    if (pid == 0) {
×
690
      execlp("xdg-open", "xdg-open", s.c_str(), nullptr);
×
691
      _exit(127);
×
692
    }
693
#else
694
    //iOS/Android/Consoles: not supported
695
    (void)path;
696
#endif
697
  }
×
698
} // geEngineSDK
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc