ui/winui/toolkit.cpp

changeset 5
83263002816f
parent 3
f154867f54dc
--- a/ui/winui/toolkit.cpp	Sun Jan 28 17:05:46 2024 +0100
+++ b/ui/winui/toolkit.cpp	Sun Jan 28 20:47:40 2024 +0100
@@ -50,6 +50,7 @@
 using namespace Microsoft::UI::Xaml::Markup;
 using namespace Windows::UI::Xaml::Interop;
 using namespace winrt::Windows::Foundation;
+using namespace Windows::UI::Core;
 
 static const char* application_name;
 
@@ -69,7 +70,11 @@
 
 static UiObject* active_window;
 
+static winrt::Microsoft::UI::Dispatching::DispatcherQueue uiDispatcherQueue = { nullptr };
+
 void ui_app_run_startup() {
+	uiDispatcherQueue = winrt::Microsoft::UI::Dispatching::DispatcherQueue::GetForCurrentThread();
+	
 	if (startup_func) {
 		startup_func(NULL, startup_data);
 	}
@@ -233,3 +238,125 @@
 
 }
 
+static void ui_job_finished(UiJob *job) {
+	UiEvent event;
+	event.obj = job->obj;
+	event.window = job->obj->window;
+	event.document = job->obj->ctx->document;
+	event.intval = 0;
+	event.eventdata = NULL;
+	job->finish_callback(&event, job->finish_data);
+}
+
+static void ui_job_thread(UiJob* job) {
+	if (!job->job_func(job->job_data) && job->finish_callback) {
+		bool isQueued = uiDispatcherQueue.TryEnqueue([job]()
+		{
+			UiEvent event;
+			event.obj = job->obj;
+			event.window = job->obj->window;
+			event.document = job->obj->ctx->document;
+			event.intval = 0;
+			event.eventdata = NULL;
+			job->finish_callback(&event, job->finish_data);
+			delete job;
+		});
+		if (!isQueued) {
+			// TODO: error or try again?
+			exit(-1);
+		}
+	}
+	else {
+		delete job;
+	}
+}
+
+UIEXPORT void ui_job(UiObject* obj, ui_threadfunc tf, void* td, ui_callback f, void* fd) {
+	UiJob* job = new UiJob;
+	job->obj = obj;
+	job->job_func = tf;
+	job->job_data = td;
+	job->finish_callback = f;
+	job->finish_data = fd;
+
+	std::thread jobThread(ui_job_thread, job);
+	jobThread.detach();
+}
+
+UIEXPORT void ui_call_mainthread(ui_threadfunc tf, void* td) {
+	bool isQueued = uiDispatcherQueue.TryEnqueue([tf, td]()
+	{
+		(void)tf(td);
+	});
+	if (!isQueued) {
+		// TODO: error or try again?
+		exit(-1);
+	}
+}
+
+static UiJob kill_job; // &kill_job indicates to stop the thread
+
+static void ui_threadpool_run(UiThreadpool* pool) {
+	for (;;) {
+		UiJob* job = pool->GetJob();
+		if (job == &kill_job) {
+			return;
+		}
+		else if (job) {
+			ui_job_thread(job);
+		}
+	}
+}
+
+UiThreadpool::UiThreadpool(int nthreads) {
+	for (int i = 0; i < nthreads; i++) {
+		std::thread thread(ui_threadpool_run, this);
+		thread.detach();
+	}
+}
+
+void UiThreadpool::EnqueueJob(UiJob* job)
+{
+	std::unique_lock<std::mutex> lock(mutex);
+	queue.push(job);
+	lock.unlock();
+	condition.notify_one();
+}
+
+UiJob* UiThreadpool::GetJob() {
+	std::unique_lock<std::mutex> lock(mutex);
+
+	UiJob* job = nullptr;
+	while (!job) {
+		if (queue.empty()) {
+			condition.wait(lock);
+			continue;
+		}
+		else
+		{
+			job = queue.front();
+			queue.pop();
+		}
+	}
+
+	return job;
+}
+
+UIEXPORT UiThreadpool* ui_threadpool_create(int nthreads) {
+	return new UiThreadpool(nthreads);
+}
+
+UIEXPORT void ui_threadpool_destroy(UiThreadpool* pool) {
+	// TODO
+}
+
+UIEXPORT void ui_threadpool_job(UiThreadpool* pool, UiObject* obj, ui_threadfunc tf, void* td, ui_callback f, void* fd) {
+	UiJob* job = new UiJob;
+	job->obj = obj;
+	job->job_func = tf;
+	job->job_data = td;
+	job->finish_callback = f;
+	job->finish_data = fd;
+	pool->EnqueueJob(job);
+}
+

mercurial