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. #define OEMRESOURCE
  7. #include <windows.h>
  8. #include <intrin.h>
  9. #define BOOST_PARAMETER_MAX_ARITY 15
  10. #include <boost/parameter.hpp>
  11. #include <boost/optional.hpp>
  12. #include <boost/variant.hpp>
  13. #include <boost/exception/all.hpp>
  14. #include <boost/utility.hpp>
  15. #include <string>
  16. #include <iostream>
  17.  
  18. #pragma comment(lib, "user32.lib")
  19. #pragma comment(lib, "gdi32.lib")
  20.  
  21. #define SBOK_ASSERT(x) \
  22.   do { if (!(x)) { std::cerr << "Assert failed: " << #x << " at " << __FILE__ << ":" << __LINE__ << std::endl; __debugbreak(); } } while (false)
  23.  
  24. #if !defined(UNICODE) || !defined(_UNICODE) || _UNICODE != 1 || UNICODE != 1
  25. # error Springbok requires UNICODE to be set.
  26. #endif
  27.  
  28. namespace detail {
  29. typedef boost::error_info<struct tag_error_msg, std::string> error_msg;
  30. typedef boost::error_info<struct tag_error_code, int> error_code;
  31.  
  32. template <typename Exception>
  33. Exception make_error(boost::optional<DWORD> code = boost::none) {
  34. DWORD real_code = (code ? *code : GetLastError());
  35. std::string message;
  36.  
  37. LPSTR buffer;
  38. DWORD result = FormatMessageA(
  39. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  40. nullptr, real_code, 0, reinterpret_cast<LPSTR>(&buffer), 0, nullptr
  41. );
  42.  
  43. if (result) {
  44. message = buffer;
  45. LocalFree(buffer);
  46. } else {
  47. message = "<FormatMessage failed>";
  48. }
  49.  
  50. return Exception() << error_msg(message) << error_code(real_code);
  51. }
  52. }
  53.  
  54. struct base_error : virtual boost::exception, virtual std::exception {};
  55. struct windowing_error : virtual base_error {};
  56.  
  57. #define SBOK_THROW_WIN(exc) BOOST_THROW_EXCEPTION(detail::make_error<exc>())
  58.  
  59. namespace window_class {
  60. enum styles {
  61. enable_double_clicks = CS_DBLCLKS,
  62. redraw_on_width_change = CS_HREDRAW,
  63. redraw_on_height_change = CS_VREDRAW,
  64. no_close_button = CS_NOCLOSE,
  65. use_shared_context = CS_CLASSDC,
  66. use_unique_context = CS_OWNDC,
  67. use_parent_context = CS_PARENTDC,
  68. no_style = 0
  69. };
  70.  
  71. namespace keywords {
  72. BOOST_PARAMETER_NAME(class_name)
  73. BOOST_PARAMETER_NAME(class_style)
  74. BOOST_PARAMETER_NAME(icon)
  75. BOOST_PARAMETER_NAME(cursor)
  76. BOOST_PARAMETER_NAME(background)
  77. BOOST_PARAMETER_NAME(menu_resource)
  78. BOOST_PARAMETER_NAME(small_icon)
  79. }
  80. };
  81.  
  82. namespace detail {
  83. LRESULT CALLBACK dispatch_wndproc(HWND, UINT, WPARAM, LPARAM);
  84. typedef boost::variant<WORD, std::wstring> resource_id;
  85.  
  86. struct resource_id_visitor : boost::static_visitor<>, private boost::noncopyable {
  87. mutable LPCWSTR& resource_id;
  88.  
  89. resource_id_visitor(LPCWSTR& resource_id) : resource_id(resource_id) {
  90. }
  91.  
  92. void operator()(const WORD& int_id) const {
  93. resource_id = MAKEINTRESOURCE(int_id);
  94. }
  95.  
  96. void operator()(const std::wstring& str_id) const {
  97. resource_id = str_id.c_str();
  98. }
  99. };
  100. }
  101.  
  102. namespace detail {
  103. struct window_impl;
  104.  
  105. struct window_class_impl {
  106. window_class_impl(
  107. const std::wstring& class_name, window_class::styles class_style, HICON icon, boost::optional<HCURSOR> cursor,
  108. boost::optional<HBRUSH> background, const detail::resource_id& menu_resource, HICON small_icon
  109. ) : name(class_name), atom(), class_info()
  110. {
  111. SecureZeroMemory(&class_info, sizeof class_info);
  112.  
  113. HCURSOR use_cursor;
  114. HBRUSH use_background;
  115.  
  116. if (cursor) {
  117. use_cursor = *cursor;
  118. } else {
  119. HANDLE arrow_cursor = LoadImage(
  120. class_info.hInstance, MAKEINTRESOURCE(OCR_NORMAL), IMAGE_CURSOR, 0, 0,
  121. LR_DEFAULTSIZE | LR_SHARED
  122. );
  123.  
  124. if (!arrow_cursor) {
  125. SBOK_THROW_WIN(windowing_error);
  126. }
  127.  
  128. use_cursor = static_cast<HCURSOR>(arrow_cursor);
  129. }
  130.  
  131. if (background) {
  132. use_background = *background;
  133. } else {
  134. use_background = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
  135. }
  136.  
  137. class_info.cbSize = sizeof class_info;
  138. class_info.style = static_cast<UINT>(class_style);
  139. class_info.lpfnWndProc = detail::dispatch_wndproc;
  140. class_info.cbClsExtra = 0;
  141. class_info.cbWndExtra = sizeof (window_impl*);
  142. class_info.hInstance = GetModuleHandle(nullptr);
  143. class_info.hIcon = icon;
  144. class_info.hCursor = use_cursor;
  145. class_info.hbrBackground = use_background;
  146. class_info.lpszClassName = class_name.c_str();
  147. class_info.hIconSm = small_icon;
  148.  
  149. boost::apply_visitor(detail::resource_id_visitor(class_info.lpszMenuName), menu_resource);
  150.  
  151. atom = RegisterClassEx(&class_info);
  152. if (!atom) {
  153. SBOK_THROW_WIN(windowing_error);
  154. }
  155. }
  156.  
  157. std::wstring get_name() const {
  158. return name;
  159. }
  160. private:
  161. std::wstring name;
  162. ATOM atom;
  163. WNDCLASSEX class_info;
  164. };
  165. }
  166.  
  167. namespace window_position {
  168. struct specific {
  169. explicit specific(int x = CW_USEDEFAULT, int y = 0) : x(x), y(y) {
  170. }
  171.  
  172. int x, y;
  173. };
  174.  
  175. struct centered {};
  176. };
  177.  
  178. namespace window {
  179. enum extended_styles {
  180. no_extended_style = 0,
  181. accept_files = WS_EX_ACCEPTFILES,
  182. is_app_window = WS_EX_APPWINDOW,
  183. has_client_edge = WS_EX_CLIENTEDGE,
  184. is_composited = WS_EX_COMPOSITED,
  185. has_context_help = WS_EX_CONTEXTHELP,
  186. is_control_parent = WS_EX_CONTROLPARENT,
  187. has_modal_frame = WS_EX_DLGMODALFRAME,
  188. is_layered = WS_EX_LAYERED,
  189. is_mdi_child = WS_EX_MDICHILD,
  190. no_inherited_layout = WS_EX_NOINHERITLAYOUT,
  191. no_parent_notify = WS_EX_NOPARENTNOTIFY,
  192. is_overlapped_ex = WS_EX_OVERLAPPEDWINDOW,
  193. has_static_edge = WS_EX_STATICEDGE,
  194. is_topmost = WS_EX_TOPMOST,
  195. has_window_edge = WS_EX_WINDOWEDGE
  196. };
  197.  
  198. enum styles {
  199. default_style = WS_OVERLAPPED,
  200. has_border = WS_BORDER,
  201. has_title_bar = WS_CAPTION,
  202. is_child = WS_CHILD,
  203. is_disabled = WS_DISABLED,
  204. has_dialog_frame = WS_DLGFRAME,
  205. has_horiz_scrollbar = WS_HSCROLL,
  206. create_minimised = WS_MINIMIZE,
  207. create_maximised = WS_MAXIMIZE,
  208. has_maximise_button = WS_MAXIMIZEBOX,
  209. has_minimise_button = WS_MINIMIZEBOX,
  210. is_overlapped = WS_OVERLAPPEDWINDOW,
  211. has_sizing_border = WS_SIZEBOX,
  212. has_system_menu = WS_SYSMENU,
  213. create_visible = WS_VISIBLE,
  214. has_vert_scrollbar = WS_VSCROLL
  215. };
  216.  
  217. namespace keywords {
  218. BOOST_PARAMETER_NAME(window_class)
  219. BOOST_PARAMETER_NAME(extended_style)
  220. BOOST_PARAMETER_NAME(name)
  221. BOOST_PARAMETER_NAME(style)
  222. BOOST_PARAMETER_NAME(position)
  223. BOOST_PARAMETER_NAME(width)
  224. BOOST_PARAMETER_NAME(height)
  225. BOOST_PARAMETER_NAME(parent)
  226. BOOST_PARAMETER_NAME(menu)
  227. }
  228. }
  229.  
  230. namespace detail {
  231. typedef boost::variant<window_position::specific, window_position::centered> window_position_variant;
  232.  
  233. // called to get x, y for CreateWindowEx
  234. struct position_create_visitor : boost::static_visitor<>, private boost::noncopyable {
  235. mutable int &x, &y;
  236. int width, height;
  237. position_create_visitor(int& x, int& y, int width, int height) : x(x), y(y), width(width), height(height) {}
  238.  
  239. void operator()(const window_position::specific& pos) const {
  240. x = pos.x;
  241. y = pos.y;
  242. }
  243.  
  244. void operator()(const window_position::centered&) const {
  245. RECT rect;
  246. GetClientRect(GetDesktopWindow(), &rect);
  247.  
  248. x = (rect.right / 2) - (width / 2);
  249. y = (rect.bottom / 2) - (height / 2);
  250. }
  251. };
  252.  
  253. struct unhandled {};
  254.  
  255. typedef boost::variant<unhandled, LRESULT> process_result;
  256.  
  257. // wraps a window
  258. struct window_impl {
  259. window_impl(
  260. const window_class_impl& window_class, window::extended_styles extended_style, const std::wstring& name,
  261. window::styles style, const window_position_variant& position, int width, int height, HWND parent, HMENU menu
  262. ) : handle()
  263. {
  264. int x, y;
  265. boost::apply_visitor(position_create_visitor(x, y, width, height), position);
  266.  
  267. std::wstring class_name = window_class.get_name();
  268. handle = CreateWindowEx(
  269. extended_style, class_name.c_str(), name.c_str(), style,
  270. x, y, width, height, parent, menu, GetModuleHandle(nullptr),
  271. nullptr
  272. );
  273.  
  274. if (handle == INVALID_HANDLE_VALUE) {
  275. SBOK_THROW_WIN(windowing_error);
  276. }
  277.  
  278. SetWindowLongPtr(handle, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
  279. UpdateWindow(handle);
  280. }
  281.  
  282. process_result process_message(UINT, WPARAM, LPARAM) {
  283. return unhandled();
  284. }
  285.  
  286. HWND handle;
  287. };
  288. }
  289.  
  290. // factory functions
  291.  
  292. BOOST_PARAMETER_FUNCTION(
  293. (detail::window_class_impl), create_window_class, window_class::keywords::tag,
  294. (required
  295. (class_name, *)
  296. )
  297. (optional
  298. (class_style, *, window_class::no_style)
  299. (icon, *, nullptr )
  300. (cursor, *, boost::none )
  301. (background, *, boost::none )
  302. (menu_resource, *, L"" )
  303. (small_icon, *, nullptr )
  304. )
  305. ) {
  306. return detail::window_class_impl(class_name, class_style, icon, cursor, background, menu_resource, small_icon);
  307. }
  308.  
  309. BOOST_PARAMETER_FUNCTION(
  310. (detail::window_impl), create_window, window::keywords::tag,
  311. (required
  312. (window_class, *)
  313. )
  314. (optional
  315. (extended_style, *, window::is_overlapped_ex)
  316. (name, *, L"")
  317. (style, *, static_cast<window::styles>(window::default_style | window::create_visible))
  318. (position, *, window_position::centered())
  319. (width, *, 800)
  320. (height, *, 600)
  321. (parent, *, nullptr)
  322. (menu, *, nullptr)
  323. )
  324. ) {
  325. return detail::window_impl(window_class, extended_style, name, style, position, width, height, parent, menu);
  326. }
  327.  
  328. namespace detail {
  329. struct dispatch_visitor : boost::static_visitor<LRESULT>, private boost::noncopyable {
  330. const HWND& handle;
  331. const UINT& message;
  332. const WPARAM& w;
  333. const LPARAM& l;
  334.  
  335. dispatch_visitor(const HWND& handle, const UINT& message, const WPARAM& w, const LPARAM& l)
  336. : handle(handle), message(message), w(w), l(l) {}
  337.  
  338. LRESULT operator()(const LRESULT& result) const {
  339. return result;
  340. }
  341.  
  342. LRESULT operator()(const unhandled&) const {
  343. return DefWindowProc(handle, message, w, l);
  344. }
  345. };
  346.  
  347. // delegates the message to process_msg member function inside window instance
  348. LRESULT CALLBACK dispatch_wndproc(HWND handle, UINT message, WPARAM w, LPARAM l) {
  349. LONG_PTR self = GetWindowLongPtr(handle, GWLP_USERDATA);
  350. //SBOK_ASSERT(self != 0);
  351.  
  352. auto real_wndproc = &window_impl::process_message;
  353. auto real_handle = reinterpret_cast<window_impl*>(self);
  354.  
  355. detail::process_result result = (real_handle->*real_wndproc)(message, w, l);
  356. return boost::apply_visitor(dispatch_visitor(handle, message, w, l), result);
  357. }
  358. }
  359.  
  360. int main() {
  361. using namespace window_class::keywords;
  362. using namespace window::keywords;
  363.  
  364. try {
  365. auto cls = create_window_class(_class_name = L"hello world", _class_style = window_class::no_style);
  366. auto wnd = create_window(_window_class = cls, _name = L"ohai");
  367.  
  368. int result = 0;
  369. MSG msg;
  370. while ((result = GetMessage(&msg, NULL, 0, 0)) != 0) {
  371. if (result == -1) {
  372. SBOK_THROW_WIN(windowing_error);
  373. } else {
  374. TranslateMessage(&msg);
  375. DispatchMessage(&msg);
  376. }
  377. }
  378. } catch (const std::exception& e) {
  379. std::cerr << boost::diagnostic_information(e) << std::endl;
  380. }
  381.  
  382. return 0;
  383. }
  384.  
Not running #stdin #stdout 0s 0KB
stdin
Standard input is empty
stdout
Standard output is empty