diff options
author | chai <chaifix@163.com> | 2018-08-07 21:53:55 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2018-08-07 21:53:55 +0800 |
commit | 926584873cd9a83458ac482c02902f4eedb1e9d3 (patch) | |
tree | 2ed019c5b8e6f391ae7a0268a200b51b7ffe293e |
-rw-r--r-- | Thread/WindowsBased/example/test.cpp | 11 | ||||
-rw-r--r-- | Thread/WindowsBased/thread.h | 342 | ||||
-rw-r--r-- | test/test.sln | 28 | ||||
-rw-r--r-- | test/test.vcxproj | 153 | ||||
-rw-r--r-- | test/test.vcxproj.filters | 27 | ||||
-rw-r--r-- | ui/ui.h | 56 | ||||
-rw-r--r-- | ui/win32.h | 331 |
7 files changed, 948 insertions, 0 deletions
diff --git a/Thread/WindowsBased/example/test.cpp b/Thread/WindowsBased/example/test.cpp new file mode 100644 index 0000000..b7b7279 --- /dev/null +++ b/Thread/WindowsBased/example/test.cpp @@ -0,0 +1,11 @@ +#include "../cpp-utils-master/ui/ui/ui.h" + +using namespace ui; + +int ui_main() +{ + window w(256, 256); + w << "Hello World!"; + w.waitkey(); + return 0; +} diff --git a/Thread/WindowsBased/thread.h b/Thread/WindowsBased/thread.h new file mode 100644 index 0000000..e8bb30d --- /dev/null +++ b/Thread/WindowsBased/thread.h @@ -0,0 +1,342 @@ +// mt - thread library to create C++ experiments -*- C++ -*- +// Copyright (C) 2010, 2012 David Capello +// +// Distributed under the terms of the New BSD License, +// see LICENSE.md for more details. + +#ifndef MT_THREAD_HEADER_FILE_INCLUDED +#define MT_THREAD_HEADER_FILE_INCLUDED + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0400 // From Windows 2000 +#endif + +#include <windows.h> + +#include <cassert> +#include <string> +#include <exception> + +namespace mt { + + ////////////////////////////////////////////////////////////////////// + // lock_guard class + + template<class Mutex> + class lock_guard { + public: + typedef Mutex mutex_type; + + explicit lock_guard(mutex_type& mutex) : m_mutex(mutex) { + m_mutex.lock(); + } + + ~lock_guard() { + m_mutex.unlock(); + } + + void lock() { + m_mutex.lock(); + } + + void unlock() { + m_mutex.unlock(); + } + + private: + mutex_type& m_mutex; + + // Non-copyable + lock_guard(const lock_guard&); + lock_guard& operator=(const lock_guard&); + }; + + ////////////////////////////////////////////////////////////////////// + // mutex class + + class mutex { + public: + mutex() { + InitializeCriticalSection(&m_cs); + } + + ~mutex() { + DeleteCriticalSection(&m_cs); + } + + void lock() { + EnterCriticalSection(&m_cs); + } + + bool try_lock() { + return TryEnterCriticalSection(&m_cs) ? true: false; + } + + void unlock() { + LeaveCriticalSection(&m_cs); + } + + private: + CRITICAL_SECTION m_cs; + + // Non-copyable + mutex(const mutex&); + mutex& operator=(const mutex&); + }; + + ////////////////////////////////////////////////////////////////////// + // condition_variable class + + class condition_variable { + public: + condition_variable() { + m_waiting_queue = + CreateSemaphore(NULL, // Security attributes + 0, // Initial count + 1000, // Maximum number of waiters (TODO) + NULL); // Unnamed semaphore + m_waiters = 0; + } + + ~condition_variable() { + // Here we could call notify_all, but, if a condition variable + // is destroyed, "there shall be no thread blocked on *this" + // (see 30.5.1 C++ working draft n3035) + assert(m_waiters == 0); + + CloseHandle(m_waiting_queue); + } + + void wait(lock_guard<mutex>& external_monitor) { + { + lock_guard<mutex> lock(m_monitor); + + assert(m_waiters >= 0); + + ++m_waiters; + external_monitor.unlock(); + } + ::WaitForSingleObject(m_waiting_queue, INFINITE); + external_monitor.lock(); + } + + void notify_one() { + lock_guard<mutex> lock(m_monitor); + + assert(m_waiters >= 0); + + // If there are one or more waiters... + if (m_waiters > 0) { + // Increment semaphore count to unlock a waiting thread + ReleaseSemaphore(m_waiting_queue, 1, NULL); + --m_waiters; + } + } + + void notify_all() { + lock_guard<mutex> lock(m_monitor); + + assert(m_waiters >= 0); + + // If there are one or more waiters... + if (m_waiters > 0) { + // Increment the semaphore to the number of waiting threads + ReleaseSemaphore(m_waiting_queue, m_waiters, NULL); + m_waiters = 0; + } + } + + private: + mutex m_monitor; // To avoid running two condition_variable member function at the same time + HANDLE m_waiting_queue; // Queue of waiting threads + LONG m_waiters; // Number of waiters in the queue + + // Non-copyable + condition_variable(const condition_variable&); + condition_variable& operator=(const condition_variable&); + }; + + ////////////////////////////////////////////////////////////////////// + // ultra-simplistic thread class implementation based on C++0x + + class thread { + public: + class details; + class id { + friend class thread; + friend class details; + + DWORD m_native_id; + id(DWORD id) : m_native_id(id) { } + public: + id() : m_native_id(0) { } + bool operator==(const id& y) const { return m_native_id == y.m_native_id; } + bool operator!=(const id& y) const { return m_native_id != y.m_native_id; } + bool operator< (const id& y) const { return m_native_id < y.m_native_id; } + bool operator<=(const id& y) const { return m_native_id <= y.m_native_id; } + bool operator> (const id& y) const { return m_native_id > y.m_native_id; } + bool operator>=(const id& y) const { return m_native_id >= y.m_native_id; } + + // TODO should we replace this with support for iostreams? + DWORD get_native_id() { return m_native_id; } + }; + + typedef HANDLE native_handle_type; + + private: + + template<class Callable> + struct f_wrapper0 { + Callable f; + f_wrapper0(const Callable& f) : f(f) { } + void operator()() { f(); } + }; + + template<class Callable, class A> + struct f_wrapper1 { + Callable f; + A a; + f_wrapper1(const Callable& f, A a) : f(f), a(a) { } + void operator()() { f(a); } + }; + + template<class Callable, class A, class B> + struct f_wrapper2 { + Callable f; + A a; + B b; + f_wrapper2(const Callable& f, A a, B b) : f(f), a(a), b(b) { } + void operator()() { f(a, b); } + }; + + template<class T> + static DWORD WINAPI thread_proxy(LPVOID data) { + T* t = (T*)data; + (*t)(); + delete t; + return 0; + } + + native_handle_type m_native_handle; + id m_id; + + public: + + // Create an instance to represent the current thread + thread() + : m_native_handle(NULL) + , m_id() { + } + + // Create a new thread without arguments + template<class Callable> + thread(const Callable& f) { + m_native_handle = + CreateThread(NULL, 0, + thread_proxy<f_wrapper0<Callable> >, + (LPVOID)new f_wrapper0<Callable>(f), + CREATE_SUSPENDED, &m_id.m_native_id); + ResumeThread(m_native_handle); + } + + // Create a new thread with one argument + template<class Callable, class A> + thread(const Callable& f, A a) { + m_native_handle = + CreateThread(NULL, 0, + thread_proxy<f_wrapper1<Callable, A> >, + (LPVOID)new f_wrapper1<Callable, A>(f, a), + CREATE_SUSPENDED, &m_id.m_native_id); + ResumeThread(m_native_handle); + } + + // Create a new thread with two arguments + template<class Callable, class A, class B> + thread(const Callable& f, A a, B b) { + m_native_handle = + CreateThread(NULL, 0, + thread_proxy<f_wrapper2<Callable, A, B> >, + (LPVOID)new f_wrapper2<Callable, A, B>(f, a, b), + CREATE_SUSPENDED, &m_id.m_native_id); + ResumeThread(m_native_handle); + } + + ~thread() { + if (joinable()) + detach(); + } + + bool joinable() const { + return + m_native_handle != NULL && + m_id.m_native_id != ::GetCurrentThreadId(); + } + + void join() { + if (joinable()) { + ::WaitForSingleObject(m_native_handle, INFINITE); + detach(); + } + } + + void detach() { + ::CloseHandle(m_native_handle); + + m_native_handle = NULL; + m_id = id(); + } + + id get_id() const { + return m_id; + } + + native_handle_type native_handle() { + return m_native_handle; + } + + class details { + public: + static id get_current_thread_id() { + return id(::GetCurrentThreadId()); + } + }; + + }; + + ////////////////////////////////////////////////////////////////////// + // this_thread namespace + + namespace this_thread { + + inline thread::id get_id() { + return thread::details::get_current_thread_id(); + } + + inline void yield() { + ::Sleep(0); + } + + // Simplified API (here we do not implement duration/time_point C++0x classes + inline void sleep_for(int milliseconds) { + ::Sleep(milliseconds); + } + + } // namespace this_thread + + ////////////////////////////////////////////////////////////////////// + // thread_guard class + + class thread_guard { + public: + explicit thread_guard(thread& t) : m_thread(t) { } + ~thread_guard() { + if (m_thread.joinable()) + m_thread.join(); + } + private: + thread& m_thread; + }; + +} // namespace mt + +#endif // MT_THREAD_HEADER_FILE_INCLUDED diff --git a/test/test.sln b/test/test.sln new file mode 100644 index 0000000..118a2d3 --- /dev/null +++ b/test/test.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test.vcxproj", "{49B3D92E-711D-45D9-8E6A-2AFE796EFF82}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {49B3D92E-711D-45D9-8E6A-2AFE796EFF82}.Debug|x64.ActiveCfg = Debug|x64 + {49B3D92E-711D-45D9-8E6A-2AFE796EFF82}.Debug|x64.Build.0 = Debug|x64 + {49B3D92E-711D-45D9-8E6A-2AFE796EFF82}.Debug|x86.ActiveCfg = Debug|Win32 + {49B3D92E-711D-45D9-8E6A-2AFE796EFF82}.Debug|x86.Build.0 = Debug|Win32 + {49B3D92E-711D-45D9-8E6A-2AFE796EFF82}.Release|x64.ActiveCfg = Release|x64 + {49B3D92E-711D-45D9-8E6A-2AFE796EFF82}.Release|x64.Build.0 = Release|x64 + {49B3D92E-711D-45D9-8E6A-2AFE796EFF82}.Release|x86.ActiveCfg = Release|Win32 + {49B3D92E-711D-45D9-8E6A-2AFE796EFF82}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/test/test.vcxproj b/test/test.vcxproj new file mode 100644 index 0000000..b25d4d2 --- /dev/null +++ b/test/test.vcxproj @@ -0,0 +1,153 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\Thread\WindowsBased\thread.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\Thread\WindowsBased\example\test.cpp" /> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{49B3D92E-711D-45D9-8E6A-2AFE796EFF82}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>test</RootNamespace> + <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="Shared"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader>Use</PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>Use</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/test/test.vcxproj.filters b/test/test.vcxproj.filters new file mode 100644 index 0000000..91a6a78 --- /dev/null +++ b/test/test.vcxproj.filters @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="源文件"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="头文件"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="资源文件"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\Thread\WindowsBased\thread.h"> + <Filter>头文件</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\Thread\WindowsBased\example\test.cpp"> + <Filter>源文件</Filter> + </ClCompile> + </ItemGroup> +</Project>
\ No newline at end of file @@ -0,0 +1,56 @@ +// ui - Basic User Interface library to do experiments -*- C++ -*- +// Copyright (C) 2010, 2012 David Capello +// +// Distributed under the terms of the New BSD License, +// see LICENSE.md for more details. + +#ifndef UI_UI_HEADER_FILE_INCLUDED +#define UI_UI_HEADER_FILE_INCLUDED + +#if WIN32 + #include "ui/win32.h" +#endif + +namespace ui { + + ////////////////////////////////////////////////////////////////////// + // window class + + class window { + public: + window(int width, int height) { + m_impl = new window_impl(width, height); + } + + ~window() { + delete m_impl; + } + + size_t width() { + return m_impl->width(); + } + + size_t height() { + return m_impl->height(); + } + + void waitkey() { + m_impl->waitkey(); + } + + window& operator<<(const std::string& text) { + m_impl->write(text); + return *this; + } + + private: + window_impl* m_impl; + + // Non-copyable + window(const window&); + window& operator=(const window&); + }; + +} // namespace ui + +#endif // UI_UI_HEADER_FILE_INCLUDED diff --git a/ui/win32.h b/ui/win32.h new file mode 100644 index 0000000..ba9e43c --- /dev/null +++ b/ui/win32.h @@ -0,0 +1,331 @@ +// ui - Basic User Interface library to do experiments -*- C++ -*- +// Copyright (C) 2010, 2012 David Capello +// +// Distributed under the terms of the New BSD License, +// see LICENSE.md for more details. + +#ifndef UI_WIN32_HEADER_FILE_INCLUDED +#define UI_WIN32_HEADER_FILE_INCLUDED + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0400 // From Windows 2000 +#endif + +#include "mt/thread.h" +#include <exception> +#include <windows.h> + +namespace ui { + + namespace win32 { + + ////////////////////////////////////////////////////////////////////// + // win32_details namespace + + class win32_details { + + template<class WindowType> + static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + WindowType* wnd = (WindowType*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (wnd != NULL) { + LRESULT result; + if (wnd->process_message(msg, wparam, lparam, result)) + return result; + } + + return DefWindowProc(hwnd, msg, wparam, lparam); + } + + public: + + template<class WindowType> + static void register_window_class() { + HINSTANCE hinstance = ::GetModuleHandle(NULL); + LPCTSTR class_name = "ui_window_class"; + WNDCLASSEX wcex; + + if (!::GetClassInfoEx(hinstance, class_name, &wcex)) { + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = &wnd_proc<WindowType>; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hinstance; + wcex.hIcon = (HICON)NULL; + wcex.hCursor = (HCURSOR)::LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1); + wcex.lpszMenuName = (LPCTSTR)NULL; + wcex.lpszClassName = class_name; + wcex.hIconSm = NULL; + + if (RegisterClassEx(&wcex) == 0) + //throw std::exception("Cannot register win32 window class"); + throw std::exception(); + } + } + + template<class WindowType> + static HWND create_window(WindowType* wnd, int width, int height) { + HINSTANCE hinstance = ::GetModuleHandle(NULL); + HWND hwnd = CreateWindowEx(WS_EX_CONTROLPARENT, + "ui_window_class", "", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, + // CW_USEDEFAULT, CW_USEDEFAULT, + width, height, + (HWND)NULL, + (HMENU)NULL, + hinstance, + NULL); + if (hwnd != NULL) { + // Set the pointer in the user-data field of the window (TODO: use an ATOM) + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)wnd); + } + return hwnd; + } + + }; + + ////////////////////////////////////////////////////////////////////// + // win32_bitmap class + + class win32_bitmap { + HBITMAP m_hbitmap; + HDC m_hdc; + + public: + + win32_bitmap(int width, int height) { + assert(width > 0 && height > 0); + + BITMAPINFOHEADER bhdr; + bhdr.biSize = sizeof(BITMAPINFOHEADER); + bhdr.biWidth = width; + bhdr.biHeight = -height; + bhdr.biPlanes = 1; + bhdr.biBitCount = 32; // 32 bpp + bhdr.biCompression = BI_RGB; + bhdr.biSizeImage = 0; + bhdr.biXPelsPerMeter = 0; + bhdr.biYPelsPerMeter = 0; + bhdr.biClrUsed = 0; + bhdr.biClrImportant = 0; + + BITMAPINFO binf; + RGBQUAD dummy = { 0, 0, 0, 0 }; + binf.bmiColors[0] = dummy; + binf.bmiHeader = bhdr; + + char* bits = NULL; + + HDC hdc = GetDC(GetDesktopWindow()); + m_hbitmap = CreateDIBSection(hdc, &binf, DIB_RGB_COLORS, + reinterpret_cast<void**>(&bits), + NULL, 0); + + if (m_hbitmap == NULL) + throw std::exception(); // TODO throw ui_exception + + m_hdc = CreateCompatibleDC(hdc); + SelectObject(m_hdc, m_hbitmap); + + // Clear the whole bitmap with a white background + { + HGDIOBJ oldPen = SelectObject(m_hdc, ::CreatePen(PS_NULL, 0, 0)); + HGDIOBJ oldBrush = SelectObject(m_hdc, ::CreateSolidBrush(RGB(255, 255, 255))); + + Rectangle(m_hdc, 0, 0, width, height); + + DeleteObject(SelectObject(m_hdc, oldPen)); + DeleteObject(SelectObject(m_hdc, oldBrush)); + } + } + + ~win32_bitmap() { + ::DeleteDC(m_hdc); + ::DeleteObject(m_hbitmap); + } + + HBITMAP hbitmap() { + return m_hbitmap; + } + + HDC hdc() { + return m_hdc; + } + + }; + + ////////////////////////////////////////////////////////////////////// + // win32_window class + + class win32_window { + HWND m_handle; + mt::thread* m_thread; + bool m_destroy; + bool m_keypressed; + bool m_closed; + win32_bitmap m_bitmap; + int m_textx, m_texty; + + struct create_window_wrapper { + win32_window* m_wnd; + int m_width; + int m_height; + + create_window_wrapper(win32_window* wnd, int width, int height) + : m_wnd(wnd) + , m_width(width) + , m_height(height) { + } + + void operator()() { + m_wnd->m_handle = win32_details::create_window(m_wnd, m_width, m_height); + + MSG msg; + + + ::ShowWindow(m_wnd->m_handle, SW_SHOWNORMAL); + ::UpdateWindow(m_wnd->m_handle); + + // Message loop while: + // 1) the window is not destroyed + // 2) the window is visible + // 3) the Windows message queue is still alive + while (!m_wnd->m_destroy && + ::IsWindowVisible(m_wnd->m_handle) && + ::GetMessage(&msg, m_wnd->m_handle, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + // Destroy the window + ::DestroyWindow(m_wnd->m_handle); + m_wnd->m_handle = NULL; + } + + }; + + friend struct create_window_wrapper; + friend class win32_details; + + public: + + win32_window(int width, int height) + : m_bitmap(width, height) + , m_textx(0) + , m_texty(0) { + win32_details::register_window_class<win32_window>(); + + m_destroy = false; + m_keypressed = false; + m_closed = false; + m_thread = new mt::thread(create_window_wrapper(this, width, height)); + + // TODO wait m_handle != NULL (use a notification, avoid 100% CPU wait) + while (m_handle == NULL) + ; + } + + ~win32_window() { + if (m_thread) { + m_destroy = true; + m_thread->join(); + delete m_thread; + } + } + + void waitkey() { + // // do something + while (!m_keypressed && !m_closed) // TODO avoid 100% CPU wait + // wait_message(); + ; + m_keypressed = false; + } + + size_t width() { + RECT rc; + ::GetWindowRect(m_handle, &rc); + return rc.right - rc.left; + } + + size_t height() { + RECT rc; + ::GetWindowRect(m_handle, &rc); + return rc.bottom - rc.top; + } + + void write(const std::string& text) { + HDC hdc = m_bitmap.hdc(); + + // Draw the text in the bitmap + TextOut(hdc, m_textx, m_texty, text.c_str(), static_cast<int>(text.size())); + + // Calculate the size of the string + RECT rc = { 0, 0, 32000, 0 }; + DrawText(hdc, text.c_str(), static_cast<int>(text.size()), &rc, DT_CALCRECT); + + m_textx += (rc.right - rc.left); // X = X + Text Width + if (m_textx > width()) { + m_texty += (rc.bottom - rc.top); + m_textx = 0; + } + + ::InvalidateRect(m_handle, NULL, FALSE); + } + + private: + + // This function is used to process + bool process_message(UINT msg, WPARAM wparam, LPARAM lparam, LRESULT& result) + { + switch (msg) { + + case WM_KEYDOWN: + m_keypressed = true; + break; + + case WM_CLOSE: + m_closed = true; + break; + + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC window_hdc = ::BeginPaint(m_handle, &ps); + HDC bitmap_hdc = m_bitmap.hdc(); + + if (!::IsRectEmpty(&ps.rcPaint)) { + BitBlt(window_hdc, + ps.rcPaint.left, ps.rcPaint.top, // X, Y (dst) + ps.rcPaint.right - ps.rcPaint.left, // Width + ps.rcPaint.bottom - ps.rcPaint.top, // Height + bitmap_hdc, + ps.rcPaint.left, ps.rcPaint.top, // X, Y (src) + SRCCOPY); + } + + ::EndPaint(m_handle, &ps); + + result = TRUE; + } + return true; + } + return false; + } + + }; + + } // namespace win32 + + typedef win32::win32_window window_impl; + +} // namespace ui + +#define ui_main() \ + WINAPI WinMain(HINSTANCE hInstance, \ + HINSTANCE hPrevInstance, \ + LPSTR lpCmdLine, \ + int nCmdShow) \ + +#endif // UI_WIN32_HEADER_FILE_INCLUDED |