|
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 |
|
41 #include "icons.h" |
|
42 |
|
43 #include "MainWindow.xaml.h" |
|
44 |
|
45 #include "App.xaml.h" |
|
46 |
|
47 using namespace winrt; |
|
48 using namespace Microsoft::UI::Xaml; |
|
49 using namespace Microsoft::UI::Xaml::Controls; |
|
50 using namespace Microsoft::UI::Xaml::XamlTypeInfo; |
|
51 using namespace Microsoft::UI::Xaml::Markup; |
|
52 using namespace Windows::UI::Xaml::Interop; |
|
53 using namespace winrt::Windows::Foundation; |
|
54 using namespace Windows::UI::Core; |
|
55 |
|
56 static const char* application_name; |
|
57 |
|
58 static ui_callback startup_func; |
|
59 static void* startup_data; |
|
60 |
|
61 static ui_callback open_func; |
|
62 void* open_data; |
|
63 |
|
64 static ui_callback exit_func; |
|
65 void* exit_data; |
|
66 |
|
67 static ui_callback appclose_fnc; |
|
68 |
|
69 static void* appclose_udata; |
|
70 |
|
71 |
|
72 static UiObject* active_window; |
|
73 |
|
74 static winrt::Microsoft::UI::Dispatching::DispatcherQueue uiDispatcherQueue = { nullptr }; |
|
75 |
|
76 void ui_app_run_startup() { |
|
77 uiDispatcherQueue = winrt::Microsoft::UI::Dispatching::DispatcherQueue::GetForCurrentThread(); |
|
78 |
|
79 if (startup_func) { |
|
80 startup_func(NULL, startup_data); |
|
81 } |
|
82 } |
|
83 |
|
84 class App : public ApplicationT<App, IXamlMetadataProvider> { |
|
85 public: |
|
86 void OnLaunched(LaunchActivatedEventArgs const&) { |
|
87 Resources().MergedDictionaries().Append(XamlControlsResources()); |
|
88 if (startup_func) { |
|
89 startup_func(NULL, startup_data); |
|
90 } |
|
91 |
|
92 //auto window = make<winui::implementation::MainWindow>(); |
|
93 //window.Activate(); |
|
94 } |
|
95 IXamlType GetXamlType(TypeName const& type) { |
|
96 return provider.GetXamlType(type); |
|
97 } |
|
98 IXamlType GetXamlType(hstring const& fullname) { |
|
99 return provider.GetXamlType(fullname); |
|
100 } |
|
101 com_array<XmlnsDefinition> GetXmlnsDefinitions() { |
|
102 return provider.GetXmlnsDefinitions(); |
|
103 } |
|
104 private: |
|
105 XamlControlsXamlMetaDataProvider provider; |
|
106 }; |
|
107 |
|
108 UiWidget::UiWidget(winrt::Microsoft::UI::Xaml::UIElement& elm) : uielement(elm) {} |
|
109 |
|
110 extern "C" void destroy_ui_window_wrapper(void* ptr) { |
|
111 UiWindow* win = (UiWindow*)ptr; |
|
112 delete win; |
|
113 } |
|
114 |
|
115 extern "C" void destroy_ui_widget_wrapper(void* ptr) { |
|
116 UiWidget* widget = (UiWidget*)ptr; |
|
117 delete widget; |
|
118 } |
|
119 |
|
120 extern "C" void destroy_ui_container_wrapper(void* ptr) { |
|
121 UiContainer* ctn = (UiContainer*)ptr; |
|
122 delete ctn; |
|
123 } |
|
124 |
|
125 void ui_context_add_window_destructor(UiContext* ctx, UiWindow* win) { |
|
126 cxMempoolRegister(ctx->mp, win, destroy_ui_window_wrapper); |
|
127 } |
|
128 |
|
129 void ui_context_add_widget_destructor(UiContext* ctx, UiWidget* widget) { |
|
130 cxMempoolRegister(ctx->mp, widget, destroy_ui_widget_wrapper); |
|
131 } |
|
132 |
|
133 void ui_context_add_container_destructor(UiContext* ctx, UiContainer *container) { |
|
134 cxMempoolRegister(ctx->mp, container, destroy_ui_container_wrapper); |
|
135 } |
|
136 |
|
137 |
|
138 UiEvent ui_create_int_event(UiObject* obj, int64_t i) { |
|
139 UiEvent evt; |
|
140 evt.obj = obj; |
|
141 evt.window = obj->window; |
|
142 evt.document = obj->ctx->document; |
|
143 evt.eventdata = nullptr; |
|
144 evt.intval = i; |
|
145 return evt; |
|
146 } |
|
147 |
|
148 |
|
149 #include <MddBootstrap.h> |
|
150 |
|
151 void ui_appsdk_bootstrap(void) { |
|
152 const UINT32 majorMinorVersion{ 0x00010002 }; |
|
153 PCWSTR versionTag{ L"" }; |
|
154 const PACKAGE_VERSION minVersion{}; |
|
155 |
|
156 const HRESULT hr = MddBootstrapInitialize(majorMinorVersion, versionTag, minVersion); |
|
157 if (FAILED(hr)) { |
|
158 exit(102); |
|
159 } |
|
160 } |
|
161 |
|
162 void ui_init(const char* appname, int argc, char** argv) { |
|
163 application_name = appname; |
|
164 |
|
165 //ui_appsdk_bootstrap(); |
|
166 |
|
167 uic_init_global_context(); |
|
168 uic_docmgr_init(); |
|
169 uic_menu_init(); |
|
170 uic_toolbar_init(); |
|
171 |
|
172 uic_load_app_properties(); |
|
173 } |
|
174 |
|
175 const char* ui_appname() { |
|
176 return application_name; |
|
177 } |
|
178 |
|
179 void ui_onstartup(ui_callback f, void* userdata) { |
|
180 startup_func = f; |
|
181 startup_data = userdata; |
|
182 } |
|
183 |
|
184 void ui_onopen(ui_callback f, void* userdata) { |
|
185 open_func = f; |
|
186 open_data = userdata; |
|
187 } |
|
188 |
|
189 void ui_onexit(ui_callback f, void* userdata) { |
|
190 exit_func = f; |
|
191 exit_data = userdata; |
|
192 } |
|
193 |
|
194 void ui_main() { |
|
195 /* |
|
196 init_apartment(); |
|
197 //Application::Start([](auto&&) {make<App>(); }); |
|
198 |
|
199 ::winrt::Microsoft::UI::Xaml::Application::Start( |
|
200 [](auto&&) |
|
201 { |
|
202 ::winrt::make<::winrt::winui::implementation::App>(); |
|
203 }); |
|
204 */ |
|
205 { |
|
206 void (WINAPI * pfnXamlCheckProcessRequirements)(); |
|
207 auto module = ::LoadLibrary(L"Microsoft.ui.xaml.dll"); |
|
208 if (module) |
|
209 { |
|
210 pfnXamlCheckProcessRequirements = reinterpret_cast<decltype(pfnXamlCheckProcessRequirements)>(GetProcAddress(module, "XamlCheckProcessRequirements")); |
|
211 if (pfnXamlCheckProcessRequirements) |
|
212 { |
|
213 (*pfnXamlCheckProcessRequirements)(); |
|
214 } |
|
215 |
|
216 ::FreeLibrary(module); |
|
217 } |
|
218 } |
|
219 |
|
220 winrt::init_apartment(winrt::apartment_type::single_threaded); |
|
221 ::winrt::Microsoft::UI::Xaml::Application::Start( |
|
222 [](auto&&) |
|
223 { |
|
224 ::winrt::make<::winrt::winui::implementation::App>(); |
|
225 }); |
|
226 } |
|
227 |
|
228 class UiWin { |
|
229 public: |
|
230 Window window; |
|
231 }; |
|
232 |
|
233 void ui_show(UiObject* obj) { |
|
234 if (obj->wobj) { |
|
235 obj->wobj->window.Activate(); |
|
236 } else if(obj->widget && obj->widget->Show) { |
|
237 obj->widget->Show(); |
|
238 } |
|
239 } |
|
240 |
|
241 void ui_close(UiObject* obj) { |
|
242 if (obj->wobj) { |
|
243 obj->wobj->window.Close(); |
|
244 } |
|
245 } |
|
246 |
|
247 static void ui_job_thread(UiJob* job) { |
|
248 if (!job->job_func(job->job_data) && job->finish_callback) { |
|
249 bool isQueued = uiDispatcherQueue.TryEnqueue([job]() |
|
250 { |
|
251 UiEvent event; |
|
252 event.obj = job->obj; |
|
253 event.window = job->obj->window; |
|
254 event.document = job->obj->ctx->document; |
|
255 event.intval = 0; |
|
256 event.eventdata = NULL; |
|
257 job->finish_callback(&event, job->finish_data); |
|
258 delete job; |
|
259 }); |
|
260 if (!isQueued) { |
|
261 // TODO: error or try again? |
|
262 exit(-1); |
|
263 } |
|
264 } |
|
265 else { |
|
266 delete job; |
|
267 } |
|
268 } |
|
269 |
|
270 UIEXPORT void ui_job(UiObject* obj, ui_threadfunc tf, void* td, ui_callback f, void* fd) { |
|
271 UiJob* job = new UiJob; |
|
272 job->obj = obj; |
|
273 job->job_func = tf; |
|
274 job->job_data = td; |
|
275 job->finish_callback = f; |
|
276 job->finish_data = fd; |
|
277 |
|
278 std::thread jobThread(ui_job_thread, job); |
|
279 jobThread.detach(); |
|
280 } |
|
281 |
|
282 UIEXPORT void ui_call_mainthread(ui_threadfunc tf, void* td) { |
|
283 bool isQueued = uiDispatcherQueue.TryEnqueue([tf, td]() |
|
284 { |
|
285 (void)tf(td); |
|
286 }); |
|
287 if (!isQueued) { |
|
288 // TODO: error or try again? |
|
289 exit(-1); |
|
290 } |
|
291 } |
|
292 |
|
293 static UiJob kill_job; // &kill_job indicates to stop the thread |
|
294 |
|
295 static void ui_threadpool_run(UiThreadpool* pool) { |
|
296 for (;;) { |
|
297 UiJob* job = pool->GetJob(); |
|
298 if (job == &kill_job) { |
|
299 return; |
|
300 } |
|
301 else if (job) { |
|
302 ui_job_thread(job); |
|
303 } |
|
304 } |
|
305 } |
|
306 |
|
307 UiThreadpool::UiThreadpool(int nthreads) { |
|
308 for (int i = 0; i < nthreads; i++) { |
|
309 std::thread thread(ui_threadpool_run, this); |
|
310 thread.detach(); |
|
311 } |
|
312 } |
|
313 |
|
314 void UiThreadpool::EnqueueJob(UiJob* job) |
|
315 { |
|
316 std::unique_lock<std::mutex> lock(mutex); |
|
317 queue.push(job); |
|
318 lock.unlock(); |
|
319 condition.notify_one(); |
|
320 } |
|
321 |
|
322 UiJob* UiThreadpool::GetJob() { |
|
323 std::unique_lock<std::mutex> lock(mutex); |
|
324 |
|
325 UiJob* job = nullptr; |
|
326 while (!job) { |
|
327 if (queue.empty()) { |
|
328 condition.wait(lock); |
|
329 continue; |
|
330 } |
|
331 else |
|
332 { |
|
333 job = queue.front(); |
|
334 queue.pop(); |
|
335 } |
|
336 } |
|
337 |
|
338 return job; |
|
339 } |
|
340 |
|
341 UIEXPORT UiThreadpool* ui_threadpool_create(int nthreads) { |
|
342 return new UiThreadpool(nthreads); |
|
343 } |
|
344 |
|
345 UIEXPORT void ui_threadpool_destroy(UiThreadpool* pool) { |
|
346 // TODO |
|
347 } |
|
348 |
|
349 UIEXPORT void ui_threadpool_job(UiThreadpool* pool, UiObject* obj, ui_threadfunc tf, void* td, ui_callback f, void* fd) { |
|
350 UiJob* job = new UiJob; |
|
351 job->obj = obj; |
|
352 job->job_func = tf; |
|
353 job->job_data = td; |
|
354 job->finish_callback = f; |
|
355 job->finish_data = fd; |
|
356 pool->EnqueueJob(job); |
|
357 } |
|
358 |
|
359 |
|
360 |
|
361 void ui_set_widget_groups(UiContext *ctx, UIWIDGET widget, const int *groups) { |
|
362 if(!groups) { |
|
363 return; |
|
364 } |
|
365 size_t ngroups = uic_group_array_size(groups); |
|
366 ui_set_widget_ngroups(ctx, widget, groups, ngroups); |
|
367 } |
|
368 |
|
369 void ui_set_widget_ngroups(UiContext *ctx, UIWIDGET widget, const int *groups, size_t ngroups) { |
|
370 if(ngroups > 0) { |
|
371 uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, groups, ngroups); |
|
372 ui_set_enabled(widget, FALSE); |
|
373 } |
|
374 } |
|
375 |
|
376 |
|
377 UIEXPORT void ui_set_enabled(UIWIDGET widget, int enabled) { |
|
378 Control ctrl = widget->uielement.as<Control>(); |
|
379 if (ctrl) { |
|
380 ctrl.IsEnabled(enabled); |
|
381 } |
|
382 } |