// vim: set et fenc=utf-8 :
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#define UNICODE 1
#define _UNICODE 1
#include <windows.h>
#define BOOST_PARAMETER_MAX_ARITY 15
#include <boost/parameter.hpp>
#include <boost/optional.hpp>
#include <boost/variant.hpp>
#include <boost/exception/all.hpp>
#include <boost/utility.hpp>
#include <string>
#pragma comment(lib, "user32.lib")
#define SBOK_ASSERT(x) do { if (!(x)) __debugbreak(); } while (false)
#if !defined(UNICODE) || !defined(_UNICODE) || _UNICODE != 1 || UNICODE != 1
# error Springbok requires UNICODE to be set.
#endif
struct windows_error_impl {
windows_error_impl(DWORD code) : code(code), message() {
LPWSTR buffer;
DWORD result = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
nullptr, code, 0, reinterpret_cast<LPWSTR>(&buffer), 0, nullptr
);
if (result) {
message = buffer;
LocalFree(buffer);
} else {
message = L"<FormatMessage failed>";
}
}
std::wstring message;
DWORD code;
};
struct windows_error : private windows_error_impl {
windows_error() : windows_error_impl(GetLastError()) {
}
explicit windows_error(DWORD code) : windows_error_impl(code) {
}
using windows_error_impl::message;
using windows_error_impl::code;
};
typedef boost::error_info<struct tag_windows_error, windows_error> windows_error_info;
struct base_error : virtual boost::exception, virtual std::exception {};
struct windowing_error : virtual base_error {};
#define SBOK_THROW_WIN(exc) BOOST_THROW_EXCEPTION(exc() << windows_error_info(windows_error()))
namespace wnd_class {
enum styles {
enable_double_clicks = CS_DBLCLKS,
redraw_on_width_change = CS_HREDRAW,
redraw_on_height_change = CS_VREDRAW,
no_close_button = CS_NOCLOSE,
use_shared_context = CS_CLASSDC,
use_unique_context = CS_OWNDC,
use_parent_context = CS_PARENTDC,
no_style = 0
};
namespace keywords {
BOOST_PARAMETER_NAME(name)
BOOST_PARAMETER_NAME(style)
BOOST_PARAMETER_NAME(icon)
BOOST_PARAMETER_NAME(cursor)
BOOST_PARAMETER_NAME(background)
BOOST_PARAMETER_NAME(menu_resource)
BOOST_PARAMETER_NAME(small_icon)
}
};
namespace detail {
LRESULT CALLBACK dispatch_wndproc(HWND, UINT, WPARAM, LPARAM);
typedef boost::variant<WORD, std::wstring> resource_id;
struct resource_id_visitor : boost::static_visitor<>, private boost::noncopyable {
LPCWSTR& resource_id;
resource_id_visitor(LPCWSTR& resource_id) : resource_id(resource_id) {
}
void operator()(WORD& int_id) {
resource_id = MAKEINTRESOURCE(int_id);
}
void operator()(std::wstring& str_id) {
resource_id = str_id.c_str();
}
};
}
struct window;
// as required by Boost.Parameter
struct window_class_impl : private boost::noncopyable {
template <typename ArgumentPack>
window_class_impl(const ArgumentPack& args) : atom(), class_info() {
using namespace wnd_class::keywords;
SecureZeroMemory(&class_info, sizeof class_info);
boost::optional<HCURSOR> opt_cursor = args[_cursor] | boost::none;
class_info.cbSize = sizeof class_info;
class_info.style = static_cast<UINT>(args[_style] | wnd_class::no_style);
class_info.lpfnWndProc = detail::dispatch_wndproc;
class_info.cbClsExtra = 0;
class_info.cbWndExtra = sizeof window*;
class_info.hInstance = GetModuleHandle(nullptr);
class_info.hIcon = args[_icon] | nullptr;
if (opt_cursor) {
class_info.hCursor = *opt_cursor;
} else {
HANDLE arrow_cursor = LoadImage(
class_info.hInstance, MAKEINTRESOURCE(OCR_NORMAL), IMAGE_CURSOR, 0, 0,
LR_DEFAULTSIZE | LR_SHARED
);
if (!arrow_cursor) {
SBOK_THROW_WIN(windowing_error);
}
class_info.hCursor = static_cast<HCURSOR>(arrow_cursor);
}
class_info.hbrBackground = args[_background] | static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
class_info.lpszClassName = std::wstring(args[_name]).c_str();
class_info.hIconSm = args[_small_icon] | nullptr;
detail::resource_id menu = args[_menu_resource] | L"";
boost::apply_visitor(detail::resource_id_visitor(class_info.lpszMenuName), menu);
atom = RegisterClassEx(&class_info);
if (!atom) {
SBOK_THROW_WIN(windowing_error);
}
}
~window_class_impl() {
if (atom) {
UnregisterClass(class_info.lpszClassName, class_info.hInstance);
}
}
ATOM atom;
WNDCLASSEX class_info;
};
// wraps a Windows window class
struct window_class : private window_class_impl {
// cursor is boost::optional, because just NULL would be ambiguous:
// I don't want to pass default cursor all the time, but passing NULL
// in WNDCLASSEX::hCursor means that app must set the cursor explicitly
// — so if boost::none is passed, system arrow cursor will used, and
// to use the aforementioned behaviour, boost::optional<HBRUSH>(NULL) must be passed
BOOST_PARAMETER_CONSTRUCTOR(
window_class, (window_class_impl), wnd_class::keywords::tag,
(required
(name, (std::wstring) )
)
(optional
(style, (wnd_class::styles) )
(icon, (HICON) )
(cursor, (boost::optional<HCURSOR>))
(background, (HBRUSH) )
(menu_resource, (detail::resource_id) )
(small_icon, (HICON) )
)
)
operator ATOM() const {
return atom;
}
};
// creates a Windows window out of a window class
struct window {
boost::optional<LRESULT> process_message(UINT, WPARAM, LPARAM) {
return boost::none;
}
};
namespace detail {
// delegates the message to process_msg member function inside window instance
LRESULT CALLBACK dispatch_wndproc(HWND handle, UINT message, WPARAM w, LPARAM l) {
LONG_PTR self = GetWindowLongPtr(handle, GWLP_USERDATA);
SBOK_ASSERT(self != 0);
auto real_wndproc = &window::process_message;
auto real_handle = reinterpret_cast<window*>(self);
auto result = (real_handle->*real_wndproc)(message, w, l);
if (result) {
return *result;
} else {
return DefWindowProc(handle, message, w, l);
}
}
}
int main() {
using namespace wnd_class::keywords;
window_class cls(_name = L"hello world");
return 0;
}