UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2023 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "pch.h" 30 31 #include "toolkit.h" 32 33 #include <cx/allocator.h> 34 #include <cx/mempool.h> 35 36 #include "../common/context.h" 37 #include "../common/document.h" 38 #include "../common/toolbar.h" 39 #include "../common/properties.h" 40 #include "../common/app.h" 41 42 #include "icons.h" 43 44 #include "MainWindow.xaml.h" 45 46 #include "App.xaml.h" 47 48 using namespace winrt; 49 using namespace Microsoft::UI::Xaml; 50 using namespace Microsoft::UI::Xaml::Controls; 51 using namespace Microsoft::UI::Xaml::XamlTypeInfo; 52 using namespace Microsoft::UI::Xaml::Markup; 53 using namespace Windows::UI::Xaml::Interop; 54 using namespace winrt::Windows::Foundation; 55 using namespace Windows::UI::Core; 56 57 static const char* application_name; 58 59 static ui_callback appclose_fnc; 60 61 static void* appclose_udata; 62 63 64 static UiObject* active_window; 65 66 static winrt::Microsoft::UI::Dispatching::DispatcherQueue uiDispatcherQueue = { nullptr }; 67 68 void ui_app_run_startup() { 69 uiDispatcherQueue = winrt::Microsoft::UI::Dispatching::DispatcherQueue::GetForCurrentThread(); 70 uic_application_startup(NULL); 71 } 72 73 class App : public ApplicationT<App, IXamlMetadataProvider> { 74 public: 75 void OnLaunched(LaunchActivatedEventArgs const&) { 76 Resources().MergedDictionaries().Append(XamlControlsResources()); 77 uic_application_startup(NULL); 78 79 //auto window = make<winui::implementation::MainWindow>(); 80 //window.Activate(); 81 } 82 IXamlType GetXamlType(TypeName const& type) { 83 return provider.GetXamlType(type); 84 } 85 IXamlType GetXamlType(hstring const& fullname) { 86 return provider.GetXamlType(fullname); 87 } 88 com_array<XmlnsDefinition> GetXmlnsDefinitions() { 89 return provider.GetXmlnsDefinitions(); 90 } 91 private: 92 XamlControlsXamlMetaDataProvider provider; 93 }; 94 95 UiWidget::UiWidget(winrt::Microsoft::UI::Xaml::UIElement& elm) : uielement(elm) {} 96 97 extern "C" void destroy_ui_window_wrapper(void* ptr) { 98 UiWindow* win = (UiWindow*)ptr; 99 delete win; 100 } 101 102 extern "C" void destroy_ui_widget_wrapper(void* ptr) { 103 UiWidget* widget = (UiWidget*)ptr; 104 delete widget; 105 } 106 107 extern "C" void destroy_ui_container_wrapper(void* ptr) { 108 UiContainer* ctn = (UiContainer*)ptr; 109 delete ctn; 110 } 111 112 void ui_context_add_window_destructor(UiContext* ctx, UiWindow* win) { 113 cxMempoolRegister(ctx->mp, win, destroy_ui_window_wrapper); 114 } 115 116 void ui_context_add_widget_destructor(UiContext* ctx, UiWidget* widget) { 117 cxMempoolRegister(ctx->mp, widget, destroy_ui_widget_wrapper); 118 } 119 120 void ui_context_add_container_destructor(UiContext* ctx, UiContainer *container) { 121 cxMempoolRegister(ctx->mp, container, destroy_ui_container_wrapper); 122 } 123 124 125 UiEvent ui_create_int_event(UiObject* obj, int64_t i) { 126 UiEvent evt; 127 evt.obj = obj; 128 evt.window = obj->window; 129 evt.document = obj->ctx->document; 130 evt.eventdata = nullptr; 131 evt.intval = i; 132 return evt; 133 } 134 135 136 #include <MddBootstrap.h> 137 138 void ui_appsdk_bootstrap(void) { 139 const UINT32 majorMinorVersion{ 0x00010002 }; 140 PCWSTR versionTag{ ""L }; 141 const PACKAGE_VERSION minVersion{}; 142 143 const HRESULT hr = MddBootstrapInitialize(majorMinorVersion, versionTag, minVersion); 144 if (FAILED(hr)) { 145 exit(102); 146 } 147 } 148 149 void ui_init(const char* appname, int argc, char** argv) { 150 application_name = appname; 151 152 //ui_appsdk_bootstrap(); 153 154 uic_init_global_context(); 155 uic_menu_init(); 156 uic_toolbar_init(); 157 158 uic_load_app_properties(); 159 } 160 161 const char* ui_appname() { 162 return application_name; 163 } 164 165 void ui_main() { 166 /* 167 init_apartment(); 168 //Application::Start([](auto&&) {make<App>(); }); 169 170 ::winrt::Microsoft::UI::Xaml::Application::Start( 171 [](auto&&) 172 { 173 ::winrt::make<::winrt::winui::implementation::App>(); 174 }); 175 */ 176 { 177 void (WINAPI * pfnXamlCheckProcessRequirements)(); 178 auto module = ::LoadLibrary("Microsoft.ui.xaml.dll"L); 179 if (module) 180 { 181 pfnXamlCheckProcessRequirements = reinterpret_cast<decltype(pfnXamlCheckProcessRequirements)>(GetProcAddress(module, "XamlCheckProcessRequirements")); 182 if (pfnXamlCheckProcessRequirements) 183 { 184 (*pfnXamlCheckProcessRequirements)(); 185 } 186 187 ::FreeLibrary(module); 188 } 189 } 190 191 winrt::init_apartment(winrt::apartment_type::single_threaded); 192 ::winrt::Microsoft::UI::Xaml::Application::Start( 193 [](auto&&) 194 { 195 ::winrt::make<::winrt::winui::implementation::App>(); 196 }); 197 } 198 199 class UiWin { 200 public: 201 Window window; 202 }; 203 204 void ui_show(UiObject* obj) { 205 if (obj->wobj) { 206 if (!obj->wobj->window.Visible()) { 207 obj->wobj->window.Activate(); 208 obj->ref++; 209 } 210 } else if(obj->widget && obj->widget->Show) { 211 obj->widget->Show(); 212 obj->ref++; // TODO: should we check if the widget is already visible? 213 } 214 } 215 216 void ui_close(UiObject* obj) { 217 if (obj->wobj) { 218 obj->wobj->window.Close(); 219 } 220 } 221 222 static void ui_job_thread(UiJob* job) { 223 if (!job->job_func(job->job_data) && job->finish_callback) { 224 bool isQueued = uiDispatcherQueue.TryEnqueue([job]() 225 { 226 UiEvent event; 227 event.obj = job->obj; 228 event.window = job->obj->window; 229 event.document = job->obj->ctx->document; 230 event.intval = 0; 231 event.eventdata = NULL; 232 job->finish_callback(&event, job->finish_data); 233 delete job; 234 }); 235 if (!isQueued) { 236 // TODO: error or try again? 237 exit(-1); 238 } 239 } 240 else { 241 delete job; 242 } 243 } 244 245 UIEXPORT void ui_job(UiObject* obj, ui_threadfunc tf, void* td, ui_callback f, void* fd) { 246 UiJob* job = new UiJob; 247 job->obj = obj; 248 job->job_func = tf; 249 job->job_data = td; 250 job->finish_callback = f; 251 job->finish_data = fd; 252 253 std::thread jobThread(ui_job_thread, job); 254 jobThread.detach(); 255 } 256 257 UIEXPORT void ui_call_mainthread(ui_threadfunc tf, void* td) { 258 bool isQueued = uiDispatcherQueue.TryEnqueue([tf, td]() 259 { 260 (void)tf(td); 261 }); 262 if (!isQueued) { 263 // TODO: error or try again? 264 exit(-1); 265 } 266 } 267 268 static UiJob kill_job; // &kill_job indicates to stop the thread 269 270 static void ui_threadpool_run(UiThreadpool* pool) { 271 for (;;) { 272 UiJob* job = pool->GetJob(); 273 if (job == &kill_job) { 274 return; 275 } 276 else if (job) { 277 ui_job_thread(job); 278 } 279 } 280 } 281 282 UiThreadpool::UiThreadpool(int nthreads) { 283 for (int i = 0; i < nthreads; i++) { 284 std::thread thread(ui_threadpool_run, this); 285 thread.detach(); 286 } 287 } 288 289 void UiThreadpool::EnqueueJob(UiJob* job) 290 { 291 std::unique_lock<std::mutex> lock(mutex); 292 queue.push(job); 293 lock.unlock(); 294 condition.notify_one(); 295 } 296 297 UiJob* UiThreadpool::GetJob() { 298 std::unique_lock<std::mutex> lock(mutex); 299 300 UiJob* job = nullptr; 301 while (!job) { 302 if (queue.empty()) { 303 condition.wait(lock); 304 continue; 305 } 306 else 307 { 308 job = queue.front(); 309 queue.pop(); 310 } 311 } 312 313 return job; 314 } 315 316 UIEXPORT UiThreadpool* ui_threadpool_create(int nthreads) { 317 return new UiThreadpool(nthreads); 318 } 319 320 UIEXPORT void ui_threadpool_destroy(UiThreadpool* pool) { 321 // TODO 322 } 323 324 UIEXPORT void ui_threadpool_job(UiThreadpool* pool, UiObject* obj, ui_threadfunc tf, void* td, ui_callback f, void* fd) { 325 UiJob* job = new UiJob; 326 job->obj = obj; 327 job->job_func = tf; 328 job->job_data = td; 329 job->finish_callback = f; 330 job->finish_data = fd; 331 pool->EnqueueJob(job); 332 } 333 334 335 336 void ui_set_widget_groups(UiContext *ctx, UIWIDGET widget, const int *groups) { 337 if(!groups) { 338 return; 339 } 340 size_t ngroups = uic_group_array_size(groups); 341 ui_set_widget_ngroups(ctx, widget, groups, ngroups); 342 } 343 344 void ui_set_widget_ngroups(UiContext *ctx, UIWIDGET widget, const int *groups, size_t ngroups) { 345 if(ngroups > 0) { 346 uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, groups, ngroups); 347 ui_set_enabled(widget, FALSE); 348 } 349 } 350 351 352 UIEXPORT void ui_set_enabled(UIWIDGET widget, int enabled) { 353 Control ctrl = widget->uielement.as<Control>(); 354 if (ctrl) { 355 ctrl.IsEnabled(enabled); 356 } 357 } 358