HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
base.hpp
1// Copyright Take Vos 2023.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
8#include "../macros.hpp"
9#include <system_error>
10#include <utility>
11#include <string>
12#include <expected>
13#include <vector>
14#include <algorithm>
15#include <bit>
16#include <exception>
17#include <cstddef>
18#include <cstdint>
19#include <limits>
20
21hi_export_module(hikogui.win32.base);
22
23hi_export namespace hi { inline namespace v1 {
24
25enum class win32_error : uint32_t {
26 success = ERROR_SUCCESS,
27 file_not_found = ERROR_FILE_NOT_FOUND,
28 more_data = ERROR_MORE_DATA,
29 invalid_data = ERROR_INVALID_DATA,
30 insufficient_buffer = ERROR_INSUFFICIENT_BUFFER,
31 status_pending = STATUS_PENDING,
32};
33
34}} // namespace hi::v1
35
36template<>
37struct std::is_error_code_enum<hi::win32_error> : std::true_type {};
38
39namespace hi { inline namespace v1 {
40
42 char const *name() const noexcept override
43 {
44 return "win32";
45 }
46
47 std::string message(int code) const override;
48
49 bool equivalent(int code, std::error_condition const & condition) const noexcept override
50 {
51 switch (static_cast<hi::win32_error>(code)) {
52 case hi::win32_error::file_not_found:
53 return condition == std::errc::no_such_file_or_directory;
54 case hi::win32_error::more_data:
55 return condition == std::errc::message_size;
56 case hi::win32_error::invalid_data:
57 return condition == std::errc::bad_message;
58 case hi::win32_error::status_pending:
59 return condition == std::errc::interrupted;
60 case hi::win32_error::insufficient_buffer:
61 return condition == std::errc::no_buffer_space;
62 default:
63 return false;
64 };
65 }
66};
67
68hi_inline auto global_win32_error_category = win32_error_category{};
69
70[[nodiscard]] hi_inline std::error_code make_error_code(win32_error code) noexcept
71{
72 return {static_cast<int>(code), global_win32_error_category};
73}
74
75[[nodiscard]] hi_inline win32_error win32_GetLastError() noexcept
76{
77 return static_cast<win32_error>(::GetLastError());
78}
79
87[[nodiscard]] hi_inline std::expected<std::string, win32_error> win32_WideCharToMultiByte(std::wstring_view s, unsigned int code_page = CP_UTF8, uint32_t flags = 0) noexcept
88{
89 if (s.empty()) {
90 // WideCharToMultiByte() does not handle empty strings, if it can not also convert the null-character.
91 return std::string{};
92 }
93
94 auto s_len = static_cast<int>(static_cast<unsigned int>(s.size()));
95 auto r_len = ::WideCharToMultiByte(code_page, flags, s.data(), s_len, nullptr, 0, nullptr, nullptr);
96 if (r_len == 0) {
97 return std::unexpected{win32_GetLastError()};
98 }
99
100 auto r = std::string(static_cast<size_t>(static_cast<std::make_signed_t<size_t>>(r_len)), '\0');
101 r.resize_and_overwrite(r_len, [&](char *p, size_t count) {
102 return ::WideCharToMultiByte(code_page, flags, s.data(), s_len, p, static_cast<int>(count), nullptr, nullptr);
103 });
104
105 if (r.empty()) {
106 return std::unexpected{win32_GetLastError()};
107 }
108
109 return r;
110}
111
119[[nodiscard]] hi_inline std::expected<std::wstring, win32_error> win32_MultiByteToWideChar(std::string_view s, unsigned int code_page = CP_UTF8, uint32_t flags = 0) noexcept
120{
121 if (s.empty()) {
122 // MultiByteToWideChar() does not handle empty strings, if it can not also convert the null-character.
123 return std::wstring{};
124 }
125
126 auto s_len = static_cast<int>(static_cast<unsigned int>(s.size()));
127 auto r_len = ::MultiByteToWideChar(code_page, flags, s.data(), s_len, nullptr, 0);
128 if (r_len == 0) {
129 return std::unexpected{win32_GetLastError()};
130 }
131
132 auto r = std::wstring{};
133 r.resize_and_overwrite(r_len, [&](wchar_t *p, size_t count) {
134 return ::MultiByteToWideChar(code_page, flags, s.data(), s_len, p, static_cast<int>(count));
135 });
136
137 if (r.empty()) {
138 return std::unexpected{win32_GetLastError()};
139 }
140
141 return r;
142}
143
153[[nodiscard]] hi_inline std::expected<std::vector<std::string>, win32_error> win32_MultiSZToStringVector(wchar_t const *first, wchar_t const *last) noexcept
154{
155 auto r = std::vector<std::string>{};
156
157 while (first != last) {
158 auto it_zero = std::find(first, last, wchar_t{0});
159 if (it_zero == last) {
160 // No termination found.
161 return std::unexpected{win32_error::invalid_data};
162 }
163
164 auto const ws = std::wstring_view{first, static_cast<std::size_t>(it_zero - first)};
165 if (ws.empty()) {
166 // The list is terminated with an empty string.
167 break;
168 }
169
170 if (auto s = win32_WideCharToMultiByte(ws)) {
171 r.push_back(*s);
172 } else {
173 return std::unexpected{s.error()};
174 }
175
176 // Continue after the zero terminator.
177 first = it_zero + 1;
178 }
179
180 return r;
181}
182
183[[nodiscard]] hi_inline std::expected<std::string, win32_error> win32_FormatMessage(win32_error error_code) noexcept
184{
185 auto const error_code_ = static_cast<DWORD>(std::to_underlying(error_code));
186
187 // FormatMessageW() is unable to tell what the buffer size should be.
188 // But 64Kbyte is the largest buffer that one should pass.
189 LPWSTR buffer = nullptr;
190 auto const result = ::FormatMessageW(
192 NULL, // source
195 reinterpret_cast<LPWSTR>(&buffer),
196 0,
197 NULL);
198
199 if (result == 0) {
200 return std::unexpected(win32_GetLastError());
201 }
202
203 auto r = win32_WideCharToMultiByte(std::wstring_view{buffer, result});
205 return r;
206}
207
208[[nodiscard]] hi_inline std::string win32_error_category::message(int code) const
209{
210 if (auto msg = win32_FormatMessage(static_cast<win32_error>(code))) {
211 return *msg;
212
213 } else {
214 throw std::system_error(msg.error());
215 }
216}
217
226[[nodiscard]] hi_inline uint32_t win32_HANDLE_to_int(HANDLE handle) noexcept
227{
228 auto i = std::bit_cast<uintptr_t>(handle);
229 if (std::cmp_greater(i, std::numeric_limits<uint32_t>::max())) {
231 }
232 return static_cast<uint32_t>(i);
233}
234
235[[nodiscard]] hi_inline HANDLE win32_int_to_HANDLE(uint32_t i) noexcept
236{
237 return std::bit_cast<HANDLE>(static_cast<uintptr_t>(i));
238}
239
240}} // namespace hi::v1
Rules for working with win32 headers.
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
The HikoGUI namespace.
Definition recursive_iterator.hpp:15
hi_inline std::expected< std::vector< std::string >, win32_error > win32_MultiSZToStringVector(wchar_t const *first, wchar_t const *last) noexcept
Convert a win32 zero terminated list of zero terminated strings.
Definition base.hpp:153
hi_inline std::expected< std::wstring, win32_error > win32_MultiByteToWideChar(std::string_view s, unsigned int code_page=CP_UTF8, uint32_t flags=0) noexcept
Convert a win32-API compatible std::wstring to a multi-byte std::string.
Definition base.hpp:119
hi_inline uint32_t win32_HANDLE_to_int(HANDLE handle) noexcept
Convert a HANDLE to a 32-bit unsigned integer.
Definition base.hpp:226
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:378
hi_inline std::expected< std::string, win32_error > win32_WideCharToMultiByte(std::wstring_view s, unsigned int code_page=CP_UTF8, uint32_t flags=0) noexcept
Convert a win32-API compatible std::wstring to a multi-byte std::string.
Definition base.hpp:87
Definition base.hpp:41
T find(T... args)
T terminate(T... args)
T unexpected(T... args)