fork download
  1. // vim: set et fenc=utf-8 :
  2. #define WIN32_LEAN_AND_MEAN
  3. #define NOMINMAX
  4. #define UNICODE 1
  5. #define _UNICODE 1
  6. #include <windows.h>
  7. #define BOOST_PARAMETER_MAX_ARITY 15
  8. #include <boost/parameter.hpp>
  9. #include <boost/optional.hpp>
  10. #include <boost/variant.hpp>
  11. #include <boost/exception/all.hpp>
  12. #include <boost/utility.hpp>
  13. #include <string>
  14.  
  15. #pragma comment(lib, "user32.lib")
  16.  
  17. #define SBOK_ASSERT(x) do { if (!(x)) __debugbreak(); } while (false)
  18.  
  19. #if !defined(UNICODE) || !defined(_UNICODE) || _UNICODE != 1 || UNICODE != 1
  20. # error Springbok requires UNICODE to be set.
  21. #endif
  22.  
  23. struct windows_error_impl {
  24. windows_error_impl(DWORD code) : code(code), message() {
  25. LPWSTR buffer;
  26. DWORD result = FormatMessage(
  27. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  28. nullptr, code, 0, reinterpret_cast<LPWSTR>(&buffer), 0, nullptr
  29. );
  30.  
  31. if (result) {
  32. message = buffer;
  33. LocalFree(buffer);
  34. } else {
  35. message = L"<FormatMessage failed>";
  36. }
  37. }
  38.  
  39. std::wstring message;
  40. DWORD code;
  41. };
  42.  
  43. struct windows_error : private windows_error_impl {
  44. windows_error() : windows_error_impl(GetLastError()) {
  45. }
  46.  
  47. explicit windows_error(DWORD code) : windows_error_impl(code) {
  48. }
  49.  
  50. using windows_error_impl::message;
  51. using windows_error_impl::code;
  52. };
  53.  
  54. typedef boost::error_info<struct tag_windows_error, windows_error> windows_error_info;
  55. struct base_error : virtual boost::exception, virtual std::exception {};
  56. struct windowing_error : virtual base_error {};
  57.  
  58. #define SBOK_THROW_WIN(exc) BOOST_THROW_EXCEPTION(exc() << windows_error_info(windows_error()))
  59.  
  60. namespace wnd_class {
  61. enum styles {
  62. enable_double_clicks = CS_DBLCLKS,
  63. redraw_on_width_change = CS_HREDRAW,
  64. redraw_on_height_change = CS_VREDRAW,
  65. no_close_button = CS_NOCLOSE,
  66. use_shared_context = CS_CLASSDC,
  67. use_unique_context = CS_OWNDC,
  68. use_parent_context = CS_PARENTDC,
  69. no_style = 0
  70. };
  71.  
  72. namespace keywords {
  73. BOOST_PARAMETER_NAME(name)
  74. BOOST_PARAMETER_NAME(style)
  75. BOOST_PARAMETER_NAME(icon)
  76. BOOST_PARAMETER_NAME(cursor)
  77. BOOST_PARAMETER_NAME(background)
  78. BOOST_PARAMETER_NAME(menu_resource)
  79. BOOST_PARAMETER_NAME(small_icon)
  80. }
  81. };
  82.  
  83. namespace detail {
  84. LRESULT CALLBACK dispatch_wndproc(HWND, UINT, WPARAM, LPARAM);
  85. typedef boost::variant<WORD, std::wstring> resource_id;
  86.  
  87. struct resource_id_visitor : boost::static_visitor<>, private boost::noncopyable {
  88. LPCWSTR& resource_id;
  89.  
  90. resource_id_visitor(LPCWSTR& resource_id) : resource_id(resource_id) {
  91. }
  92.  
  93. void operator()(WORD& int_id) {
  94. resource_id = MAKEINTRESOURCE(int_id);
  95. }
  96.  
  97. void operator()(std::wstring& str_id) {
  98. resource_id = str_id.c_str();
  99. }
  100. };
  101. }
  102.  
  103. struct window;
  104.  
  105. // as required by Boost.Parameter
  106. struct window_class_impl : private boost::noncopyable {
  107. template <typename ArgumentPack>
  108. window_class_impl(const ArgumentPack& args) : atom(), class_info() {
  109. using namespace wnd_class::keywords;
  110.  
  111. SecureZeroMemory(&class_info, sizeof class_info);
  112.  
  113. boost::optional<HCURSOR> opt_cursor = args[_cursor] | boost::none;
  114.  
  115. class_info.cbSize = sizeof class_info;
  116. class_info.style = static_cast<UINT>(args[_style] | wnd_class::no_style);
  117. class_info.lpfnWndProc = detail::dispatch_wndproc;
  118. class_info.cbClsExtra = 0;
  119. class_info.cbWndExtra = sizeof window*;
  120. class_info.hInstance = GetModuleHandle(nullptr);
  121. class_info.hIcon = args[_icon] | nullptr;
  122. if (opt_cursor) {
  123. class_info.hCursor = *opt_cursor;
  124. } else {
  125. HANDLE arrow_cursor = LoadImage(
  126. class_info.hInstance, MAKEINTRESOURCE(OCR_NORMAL), IMAGE_CURSOR, 0, 0,
  127. LR_DEFAULTSIZE | LR_SHARED
  128. );
  129. if (!arrow_cursor) {
  130. SBOK_THROW_WIN(windowing_error);
  131. }
  132. class_info.hCursor = static_cast<HCURSOR>(arrow_cursor);
  133. }
  134. class_info.hbrBackground = args[_background] | static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
  135. class_info.lpszClassName = std::wstring(args[_name]).c_str();
  136. class_info.hIconSm = args[_small_icon] | nullptr;
  137.  
  138. detail::resource_id menu = args[_menu_resource] | L"";
  139. boost::apply_visitor(detail::resource_id_visitor(class_info.lpszMenuName), menu);
  140.  
  141. atom = RegisterClassEx(&class_info);
  142. if (!atom) {
  143. SBOK_THROW_WIN(windowing_error);
  144. }
  145. }
  146.  
  147. ~window_class_impl() {
  148. if (atom) {
  149. UnregisterClass(class_info.lpszClassName, class_info.hInstance);
  150. }
  151. }
  152.  
  153. ATOM atom;
  154. WNDCLASSEX class_info;
  155. };
  156.  
  157. // wraps a Windows window class
  158. struct window_class : private window_class_impl {
  159. // cursor is boost::optional, because just NULL would be ambiguous:
  160. // I don't want to pass default cursor all the time, but passing NULL
  161. // in WNDCLASSEX::hCursor means that app must set the cursor explicitly
  162. // — so if boost::none is passed, system arrow cursor will used, and
  163. // to use the aforementioned behaviour, boost::optional<HBRUSH>(NULL) must be passed
  164. BOOST_PARAMETER_CONSTRUCTOR(
  165. window_class, (window_class_impl), wnd_class::keywords::tag,
  166.  
  167. (required
  168. (name, (std::wstring) )
  169. )
  170. (optional
  171. (style, (wnd_class::styles) )
  172. (icon, (HICON) )
  173. (cursor, (boost::optional<HCURSOR>))
  174. (background, (HBRUSH) )
  175. (menu_resource, (detail::resource_id) )
  176. (small_icon, (HICON) )
  177. )
  178. )
  179.  
  180. operator ATOM() const {
  181. return atom;
  182. }
  183. };
  184.  
  185. // creates a Windows window out of a window class
  186. struct window {
  187. boost::optional<LRESULT> process_message(UINT, WPARAM, LPARAM) {
  188. return boost::none;
  189. }
  190. };
  191.  
  192. namespace detail {
  193. // delegates the message to process_msg member function inside window instance
  194. LRESULT CALLBACK dispatch_wndproc(HWND handle, UINT message, WPARAM w, LPARAM l) {
  195. LONG_PTR self = GetWindowLongPtr(handle, GWLP_USERDATA);
  196. SBOK_ASSERT(self != 0);
  197.  
  198. auto real_wndproc = &window::process_message;
  199. auto real_handle = reinterpret_cast<window*>(self);
  200. auto result = (real_handle->*real_wndproc)(message, w, l);
  201.  
  202. if (result) {
  203. return *result;
  204. } else {
  205. return DefWindowProc(handle, message, w, l);
  206. }
  207. }
  208. }
  209.  
  210. int main() {
  211. using namespace wnd_class::keywords;
  212. window_class cls(_name = L"hello world");
  213.  
  214. return 0;
  215. }
  216.  
Not running #stdin #stdout 0s 0KB
stdin
Standard input is empty
stdout
Standard output is empty