From d515d522a5123a7bda5f413ec7a933560a5f5337 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=98=99=E2=97=A6=20The=20Tablet=20=E2=9D=80=20GamerGirla?= =?UTF-8?q?ndCo=20=E2=97=A6=E2=9D=A7?= Date: Wed, 21 Jan 2026 16:22:10 -0500 Subject: [PATCH] feat(framework/examples): add doom example --- .gitmodules | 4 + example/CMakeLists.txt | 1 + example/_all/CMakeLists.txt | 1 + example/_all/include/defs/doom.hpp | 32 +++ example/_all/include/lib_all.hpp | 2 + example/ultimate-artblock/3rdparty/libdoom | 1 + example/ultimate-artblock/CMakeLists.txt | 37 +++ example/ultimate-artblock/src/doom_proxy.c | 189 ++++++++++++ example/ultimate-artblock/src/doom_view.cpp | 32 +++ .../ultimate-artblock/src/include/common.h | 54 ++++ .../src/include/doom_view.hpp | 10 + .../src/include/toon_doom.hpp | 269 ++++++++++++++++++ .../ultimate-artblock/src/include/util.hpp | 3 + example/ultimate-artblock/src/util.cpp | 22 ++ 14 files changed, 657 insertions(+) create mode 100644 .gitmodules create mode 100644 example/_all/include/defs/doom.hpp create mode 160000 example/ultimate-artblock/3rdparty/libdoom create mode 100644 example/ultimate-artblock/CMakeLists.txt create mode 100644 example/ultimate-artblock/src/doom_proxy.c create mode 100644 example/ultimate-artblock/src/doom_view.cpp create mode 100644 example/ultimate-artblock/src/include/common.h create mode 100644 example/ultimate-artblock/src/include/doom_view.hpp create mode 100644 example/ultimate-artblock/src/include/toon_doom.hpp create mode 100644 example/ultimate-artblock/src/include/util.hpp create mode 100644 example/ultimate-artblock/src/util.cpp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ff6b44e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "example/ultimate-artblock/3rdparty/libdoom"] + path = example/ultimate-artblock/3rdparty/libdoom + url = https://git.tablet.sh/tablet/libdoom + branch = master diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index e6edc42..f0eadb6 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -36,4 +36,5 @@ set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL") find_package(Qt6 REQUIRED COMPONENTS Core Widgets Gui Core5Compat Xml QUIET) add_subdirectory(simple) +add_subdirectory(ultimate-artblock) add_subdirectory(_all) \ No newline at end of file diff --git a/example/_all/CMakeLists.txt b/example/_all/CMakeLists.txt index 807a29a..38f8148 100644 --- a/example/_all/CMakeLists.txt +++ b/example/_all/CMakeLists.txt @@ -21,4 +21,5 @@ set_target_properties(all-examples PROPERTIES AUTORCC ON ) target_link_libraries(all-examples PUBLIC libtoonboom_static) +target_link_libraries(all-examples PRIVATE doom-example) target_link_libraries(all-examples PRIVATE simple-example) \ No newline at end of file diff --git a/example/_all/include/defs/doom.hpp b/example/_all/include/defs/doom.hpp new file mode 100644 index 0000000..6744a06 --- /dev/null +++ b/example/_all/include/defs/doom.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include "./base.hpp" +#include + +class DoomExample : public BaseExample { +public: + DoomExample() : BaseExample() { + } + ~DoomExample() {} + + std::function + run() override { + return + [this](QScriptContext *context, QScriptEngine *engine) -> QScriptValue { + auto lm = PLUG_Services::getLayoutManager(); + if(doomView == nullptr) { + doomView = new DoomView(); + lm->addArea("DoomView", doomView->displayName(), doomView, true, true, false, QSize(320, 200), true, false, true, true); + } + lm->raiseArea("DoomView", nullptr, true, QPoint(2020, 100)); + auto widget = + dynamic_cast(doomView->getWidget()); + widget->start(); + return engine->undefinedValue(); + }; + } + QString jsName() override { return "runDoom"; } + +private: + DoomView *doomView = nullptr; +}; \ No newline at end of file diff --git a/example/_all/include/lib_all.hpp b/example/_all/include/lib_all.hpp index a0cd5dd..9c0eb3e 100644 --- a/example/_all/include/lib_all.hpp +++ b/example/_all/include/lib_all.hpp @@ -7,12 +7,14 @@ #include #include "./defs/base.hpp" #include "./defs/simple.hpp" +#include "./defs/doom.hpp" class ToonBoomExamples { public: ToonBoomExamples() { addExample(new SimpleExample()); addExample(new ToolbarExample()); + addExample(new DoomExample()); } void addExample(BaseExample *example) { examples.push_back(example); } QScriptValue getExamples(QScriptEngine *engine) { diff --git a/example/ultimate-artblock/3rdparty/libdoom b/example/ultimate-artblock/3rdparty/libdoom new file mode 160000 index 0000000..6e97591 --- /dev/null +++ b/example/ultimate-artblock/3rdparty/libdoom @@ -0,0 +1 @@ +Subproject commit 6e975919c8c04f299aec7b06409f7f2222c5b2b7 diff --git a/example/ultimate-artblock/CMakeLists.txt b/example/ultimate-artblock/CMakeLists.txt new file mode 100644 index 0000000..a3b2fa4 --- /dev/null +++ b/example/ultimate-artblock/CMakeLists.txt @@ -0,0 +1,37 @@ +# include(../common.cmake) + +file(GLOB_RECURSE DOOM_EXAMPLE_HEADERS CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/src/include/*.hpp" +) + +file(GLOB_RECURSE DOOM_EXAMPLE_SOURCES CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cc" + "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cxx" +) + + +list(FILTER DOOM_EXAMPLE_SOURCES EXCLUDE REGEX "/(out|build|cmake-build-|CMakeFiles|doom_proxy)/") +list(FILTER DOOM_EXAMPLE_HEADERS EXCLUDE REGEX "/(out|build|cmake-build-|CMakeFiles|doom_proxy)/") +message(STATUS "DOOM_EXAMPLE_SOURCES: ${DOOM_EXAMPLE_SOURCES}") +# add_library(libdoom OBJECT "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/libdoom/doom.c" ) + +add_library(libdoom OBJECT "${CMAKE_CURRENT_SOURCE_DIR}/src/doom_proxy.c") +set_target_properties(libdoom PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_compile_definitions(libdoom PUBLIC "APP_WINDOWS" "__BYTEBOOL__=1" "ALLOW_MOUSE=0") +target_include_directories(libdoom PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/libdoom/linuxdoom-1.10) +target_include_directories(libdoom PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/libdoom/libs_win32) +target_compile_options(libdoom PUBLIC "/Zc:gotoScope-" "/Zc:strictStrings-" "/wd4244" "/wd4267" "/wd4838" "/wd4430" "/wd4996" "/wd4311" "/wd4113" "/showIncludes") + +add_library(doom-example STATIC ${DOOM_EXAMPLE_SOURCES} ${DOOM_EXAMPLE_HEADERS} $) +target_compile_definitions(doom-example PRIVATE "APP_WINDOWS" "__BYTEBOOL__=1") +target_compile_options(doom-example PUBLIC "/std:c++20" "/Zc:gotoScope-" "/showIncludes") +target_include_directories(doom-example PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src/include) +target_include_directories(doom-example PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/libdoom/libs_win32) +target_include_directories(doom-example PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/libdoom/linuxdoom-1.10) +target_link_libraries(doom-example PRIVATE libtoonboom_static) +set_target_properties(doom-example PROPERTIES + AUTOMOC ON + AUTOUIC ON + AUTORCC ON + ) diff --git a/example/ultimate-artblock/src/doom_proxy.c b/example/ultimate-artblock/src/doom_proxy.c new file mode 100644 index 0000000..8b36552 --- /dev/null +++ b/example/ultimate-artblock/src/doom_proxy.c @@ -0,0 +1,189 @@ + +#ifndef __cplusplus +// typedef enum {false, true} boolean; +#define false 0 +#define true 1 +typedef unsigned char byte; +#endif +#define MY_APP_FATAL_ERROR(ctx, message) { printf( "FATAL ERROR: %s\n", message ); MessageBoxA( 0, message, "Fatal Error!", MB_OK | MB_ICONSTOP ); _flushall(); } + +#define APP_LOG(ctx, level, message) printf( "%s\n", message ) +// #include +// #undef APP_IMPLEMENTATION +#define APP_IMPLEMENTATION +#include +#undef APP_IMPLEMENTATION +#ifdef _WIN32 +#define strcasecmp stricmp +#define strncasecmp strnicmp +#else +#include +#endif + +#define CONCAT_IMPL( x, y ) x##y +#define CONCAT( x, y ) CONCAT_IMPL( x, y ) +#define rcsid CONCAT( rcsid, __COUNTER__ ) + + +#include +#include +#include + +int doom_access( char const* _FileName, int _AccessMode ) { + FILE* f = fopen( _FileName, "rb" ); + if( f ) { + fclose(f); + return 0; + } + return 1; +} +#ifdef _WIN32 + #include +#else + #include +#endif + +#undef access +#define access doom_access + +#define open doom_open +#define close doom_close + +#undef LoadMenu +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#undef BG +#include +#include +#define channels xchannels +#include +#undef channels +#include +#include +#define anim_t wi_anim_t +#define anims wi_anims +#define time wi_time +#include +#undef anims +#undef anim_t +#undef time +#include + +#undef open +#undef close + +#include +#include +#define strupr xstrupr +#include +#undef strupr + +#include + +#undef MAXCHAR +#undef MAXSHORT +#undef MAXINT +#undef MAXLONG +#undef MINCHAR +#undef MINSHORT +#undef MININT +#undef MINLONG + +#include +#include +#include +#ifdef __wasm__ +#define APP_WASM +#define WA_CORO_IMPLEMENT_NANOSLEEP +#else +#define APP_WINDOWS +#endif +#define boolean HACK_TO_MAKE_BOOLEAN_NOT_BE_DEFINED + +#define FRAMETIMER_IMPLEMENTATION +#include + +#define CRTEMU_IMPLEMENTATION +#include + +#ifndef __wasm__ +#define THREAD_IMPLEMENTATION +#if defined( __TINYC__ ) + typedef struct _RTL_CONDITION_VARIABLE { PVOID Ptr; } RTL_CONDITION_VARIABLE, *PRTL_CONDITION_VARIABLE; + typedef RTL_CONDITION_VARIABLE CONDITION_VARIABLE, *PCONDITION_VARIABLE; + static VOID (*InitializeConditionVariable)( PCONDITION_VARIABLE ); + static VOID (*WakeConditionVariable)( PCONDITION_VARIABLE ); + static BOOL (*SleepConditionVariableCS)( PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD ); +#endif +#include +#undef THREAD_IMPLEMENTATION +#endif + +#undef boolean + +#define MUS_IMPLEMENTATION +#include + +#define TSF_IMPLEMENTATION +#define TSF_POW pow +#define TSF_POWF (float)pow +#define TSF_EXPF (float)exp +#define TSF_LOG log +#define TSF_TAN tan +#define TSF_LOG10 log10 +#define TSF_SQRT (float)sqrt +#define TSF_SQRTF (float)sqrt +#include +#include + +#include + +#include +#include +#include + diff --git a/example/ultimate-artblock/src/doom_view.cpp b/example/ultimate-artblock/src/doom_view.cpp new file mode 100644 index 0000000..87e59cf --- /dev/null +++ b/example/ultimate-artblock/src/doom_view.cpp @@ -0,0 +1,32 @@ +#include "./include/doom_view.hpp" +#include "common.h" + +DoomView::DoomView() : TUWidgetLayoutViewBase() {} + +DoomView::~DoomView() {} + +QString DoomView::displayName() const { return "Doom!?"; } + +ToonDoomWidget *DoomView::createWidget() { return new ToonDoomWidget(); } + +app_proc_t real_app_proc = app_proc; + +namespace toon_doom { +int app_proc(app_t *app, void *user_data) { + APP_U32 canvas[320 * 200]; + memset(canvas, 0xc0, sizeof(canvas)); + auto asDoomThread = reinterpret_cast(user_data); + asDoomThread->setApp(app); + // return 0; + auto result = real_app_proc(app, user_data); + thread_atomic_int_store(&app_running, 0); + std::cout << "exiting app_proc" << std::endl; + return result; +} +int app_proc_thread(void *user_data) { + auto asDoomThread = reinterpret_cast(user_data); + asDoomThread->setId(GetCurrentThreadId()); + auto result = app_run(app_proc, user_data, nullptr, nullptr, nullptr); + return result; +} +} // namespace toon_doom \ No newline at end of file diff --git a/example/ultimate-artblock/src/include/common.h b/example/ultimate-artblock/src/include/common.h new file mode 100644 index 0000000..51f74cf --- /dev/null +++ b/example/ultimate-artblock/src/include/common.h @@ -0,0 +1,54 @@ +#ifndef COMMON_H +#define COMMON_H +#pragma warning(disable : 4113) +#pragma warning(disable : 4311) +#pragma warning(disable : 4047) +#pragma warning(disable : 4024) +#pragma warning(disable : 4312) +#pragma warning(disable : 4020) +#pragma warning(disable : 4700) +#pragma warning(disable : 4133) +#pragma warning(disable : 4142) +#pragma warning(disable : 4005) +#pragma warning(disable : 4244) +#pragma warning(disable : 4267) +#pragma warning(disable : 4838) +#pragma warning(disable : 4430) +#pragma warning(disable : 4996) +#define APP_WINDOWS +#if defined(__BYTEBOOL__) +typedef unsigned char boolean; +typedef unsigned char byte; +#endif +#ifndef __cplusplus +#define true 1 +#define false 0 +#endif +#define MY_APP_FATAL_ERROR(ctx, message) { printf( "FATAL ERROR: %s\n", message ); MessageBoxA( 0, message, "Fatal Error!", MB_OK | MB_ICONSTOP ); _flushall(); } + +#define APP_LOG(ctx, level, message) printf( "%s\n", message ) +#ifdef __cplusplus +extern "C" { + +void D_DoomMain(void); +#include +#undef APP_FATAL_ERROR +#define APP_FATAL_ERROR MY_APP_FATAL_ERROR +#include +extern thread_mutex_t mus_mutex; +extern thread_signal_t vblank_signal; +extern thread_atomic_int_t app_running; +extern int maketic; +extern int gametic; +void M_StartControlPanel (void); +extern char** myargv; +extern int myargc; +int app_proc(app_t *app, void *user_data); +void M_QuitResponse(int ch); +} +#else +#include +#undef APP_FATAL_ERROR +#define APP_FATAL_ERROR MY_APP_FATAL_ERROR +#endif +#endif \ No newline at end of file diff --git a/example/ultimate-artblock/src/include/doom_view.hpp b/example/ultimate-artblock/src/include/doom_view.hpp new file mode 100644 index 0000000..c3e6e13 --- /dev/null +++ b/example/ultimate-artblock/src/include/doom_view.hpp @@ -0,0 +1,10 @@ +#pragma once +#include "./toon_doom.hpp" +class DoomView : public TUWidgetLayoutViewBase { +public: + DoomView(); + ~DoomView() override; + QString displayName() const override; +protected: + ToonDoomWidget *createWidget() override; +}; \ No newline at end of file diff --git a/example/ultimate-artblock/src/include/toon_doom.hpp b/example/ultimate-artblock/src/include/toon_doom.hpp new file mode 100644 index 0000000..d68dd77 --- /dev/null +++ b/example/ultimate-artblock/src/include/toon_doom.hpp @@ -0,0 +1,269 @@ +#pragma once +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "./common.h" +#include "./util.hpp" + +typedef int (*app_proc_t)(app_t *, void *); + +namespace toon_doom { +int app_proc(app_t *app, void *user_data); +int app_proc_thread(void *user_data); +}; // namespace toon_doom +class ToonDoomWidget; + +class DoomThread : public QThread { + Q_OBJECT +public: + DoomThread(ToonDoomWidget *widget, QObject *parent = nullptr) + : QThread(parent) { + m_widget = widget; + } + ~DoomThread() { + M_QuitResponse('y'); + thread_atomic_int_store(&app_running, 0); + maketic = 0; + gametic = 0; + if (isRunning()) { + terminate(); + } + } + void startGame(app_t *app) { emit gameStarted(m_widget, app); } + void setApp(app_t *app) { this->app = app; } + DWORD getId() { return tid; } + void setId(DWORD tid) { this->tid = tid; } + +private: +signals: + void gameStarted(ToonDoomWidget *widget, app_t *app); + void gameExited(const int &result); + +protected: + QPointer m_widget; + void run() override { + tid = GetCurrentThreadId(); + myargc = 0; + myargv = nullptr; + thread_signal_init(&vblank_signal); + thread_mutex_init(&mus_mutex); + thread_atomic_int_store(&app_running, 1); + threadPtr = thread_create(&toon_doom::app_proc_thread, this, + THREAD_STACK_SIZE_DEFAULT); + int result = + thread_signal_wait(&vblank_signal, THREAD_SIGNAL_WAIT_INFINITE); + startGame(app); + D_DoomMain(); + emit gameExited(result); + } + thread_ptr_t threadPtr = nullptr; + DWORD tid = 0; + app_t *app = nullptr; +}; + +class ToonDoomWidget : public QWidget { + Q_OBJECT +public: + ToonDoomWidget(QWidget *parent = nullptr) : QWidget(parent) { + setPalette(QPalette(QColor(0, 0, 0))); + setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + setAutoFillBackground(true); + setMinimumSize(320, 200); + setWindowTitle("Doom!?"); + m_layout = new QVBoxLayout(this); + m_layout->setAlignment(Qt::AlignCenter); + m_layout->setContentsMargins(0, 0, 0, 0); + m_layout->setSpacing(0); + setFocusPolicy(Qt::StrongFocus); + } + ~ToonDoomWidget() { + QObject::disconnect(static_cast(QApplication::instance()), + &QApplication::focusChanged, this, + &ToonDoomWidget::focusChanged); + auto inp = + AttachThreadInput(GetCurrentThreadId(), doomThread->getId(), false); + util::debug::out << "~ToonDoomWidget AttachThreadInput: " << inp + << std::endl; + delete doomThread; + } + void setApp(app_t *app) { + this->app = app; + init(); + } + app_t *getApp() const { return app; } + +public slots: + void start() { + if (doomThread != nullptr && doomThread->isRunning()) { + return; + } + doomThread = new DoomThread(this, this); + connect(doomThread, &DoomThread::gameExited, this, + &ToonDoomWidget::gameExited); + connect(doomThread, &DoomThread::gameStarted, this, + &ToonDoomWidget::gameStarted); + doomThread->start(); + } + void gameStarted(ToonDoomWidget *widget, app_t *app) { widget->setApp(app); } + void gameExited(const int &result) {} + +private: + QWidget *doomWidget = nullptr; + app_t *app = nullptr; + QWindow *m_window = nullptr; + DoomThread *doomThread = nullptr; + QVBoxLayout *m_layout = nullptr; + +protected: + void resizeEvent(QResizeEvent *event) override { + QWidget::resizeEvent(event); + updateGeometry(); + } + void moveEvent(QMoveEvent *event) override { + QWidget::moveEvent(event); + updateGeometry(); + } + void focusInEvent(QFocusEvent *event) override { + util::debug::out << "focusInEvent: " << event->type() << std::endl; + if (app) { + app->has_focus = true; + SetFocus(app->hwnd); + } + QWidget::focusInEvent(event); + } + + bool eventFilter(QObject *obj, QEvent *event) override { + QMetaEnum metaEnum = QMetaEnum::fromType(); + util::debug::out << "eventFilter: " << metaEnum.valueToKey(event->type()) + << " obj: " << obj->metaObject()->className() << std::endl; + if (event->type() == QEvent::WindowActivate || + (event->type() == QEvent::FocusIn && obj != this)) { + this->setFocus(Qt::OtherFocusReason); + } + if (obj == this && (event->type() == QEvent::FocusOut || + event->type() == QEvent::FocusAboutToChange)) { + event->ignore(); + setFocus(); + return true; + } + return QWidget::eventFilter(obj, event); + } + bool event(QEvent *event) override { + if (event->type() == QEvent::FocusOut || + event->type() == QEvent::FocusAboutToChange) { + util::debug::out << "focus out" << std::endl; + setEnabled(true); + if (event->type() == QEvent::FocusOut) { + auto asFocusEvent = static_cast(event); + util::debug::out << "focus out reason: " << asFocusEvent->reason() + << std::endl; + if (asFocusEvent->reason() == Qt::OtherFocusReason) { + event->ignore(); + setFocus(); + QApplication::processEvents(); + } + } else if (event->type() == QEvent::FocusAboutToChange) { + event->ignore(); + } + return true; + } + return QWidget::event(event); + } + +private: + std::pair createEscapeKeyEvents() { + return std::pair( + new QKeyEvent(QEvent::KeyPress, Qt::Key_Escape, Qt::NoModifier), + new QKeyEvent(QEvent::KeyRelease, Qt::Key_Escape, Qt::NoModifier)); + } + + bool isMouseMoveEvent(QEvent *event) { + return event->type() == QEvent::MouseMove || + event->type() == QEvent::Enter || event->type() == QEvent::Leave || + event->type() == QEvent::TabletMove || + event->type() == QEvent::NonClientAreaMouseMove || + event->type() == QEvent::GrabMouse || + event->type() == QEvent::HoverMove || + event->type() == QEvent::HoverEnter || + event->type() == QEvent::HoverLeave || + event->type() == QEvent::GraphicsSceneHoverMove || + event->type() == QEvent::GraphicsSceneHoverEnter || + event->type() == QEvent::GraphicsSceneHoverLeave || + event->type() == QEvent::GraphicsSceneMouseMove || + event->type() == QEvent::GraphicsSceneLeave; + } + std::set m_ancestors; +private slots: + void updateGeometry() { + if (m_window != nullptr) { + doomWidget->setGeometry(this->rect()); + m_window->setGeometry(doomWidget->rect()); + } + } + void windowClosed() { + util::debug::out << "window closed" << std::endl; + delete doomThread; + doomThread = nullptr; + } + void init() { + m_window = QWindow::fromWinId(WId(app->hwnd)); + Qt::WindowFlags nflags = Qt::WindowType::Window | Qt::WindowType::Widget | + Qt::WindowType::FramelessWindowHint | + Qt::CustomizeWindowHint; + const auto dpr = m_window->devicePixelRatio(); + // m_window->setFlags(m_window->flags() | nflags); + m_window->setGeometry(this->rect()); + connect(m_window, &QWindow::close, this, &ToonDoomWidget::windowClosed); + doomWidget = + QWidget::createWindowContainer(m_window, this, Qt::WindowType::Widget); + doomWidget->setSizePolicy(QSizePolicy::MinimumExpanding, + QSizePolicy::MinimumExpanding); + util::debug::out << "doomwidget == window: " + << (doomWidget->windowHandle() == m_window) << std::endl; + auto flags = doomWidget->windowFlags(); + + doomWidget->hide(); + m_layout->addWidget(doomWidget, 1); + util::debug::out << "doom thread id: " << doomThread->getId() << std::endl; + auto inp = + AttachThreadInput(GetCurrentThreadId(), doomThread->getId(), true); + util::debug::out << "AttachThreadInput: " << inp << std::endl; + doomWidget->show(); + auto parentWindow = window(); + updateGeometry(); + + { + // add widget hierarchy to a set for focus tracking + QWidget *p = doomWidget; + while (p != nullptr) { + util::debug::out << "widget @ " << util::debug::addrToHex(p) << ": " + << p->metaObject()->className() << " parent: " + << util::debug::addrToHex(p->parentWidget()) + << std::endl; + m_ancestors.insert(p); + + p = p->parentWidget(); + } + } + installEventFilter(this); + doomWidget->setFocusPolicy(Qt::StrongFocus); + QObject::connect(static_cast(QApplication::instance()), + &QApplication::focusChanged, this, + &ToonDoomWidget::focusChanged); + } + + void focusChanged(QWidget *old, QWidget *now) { + if (m_ancestors.contains(old) && !m_ancestors.contains(now)) { + app->has_focus = false; + M_StartControlPanel(); + } + } +}; \ No newline at end of file diff --git a/example/ultimate-artblock/src/include/util.hpp b/example/ultimate-artblock/src/include/util.hpp new file mode 100644 index 0000000..cc5d219 --- /dev/null +++ b/example/ultimate-artblock/src/include/util.hpp @@ -0,0 +1,3 @@ +#include + +void sendEscapeKeyToWindow(HWND hwnd); \ No newline at end of file diff --git a/example/ultimate-artblock/src/util.cpp b/example/ultimate-artblock/src/util.cpp new file mode 100644 index 0000000..d264cc2 --- /dev/null +++ b/example/ultimate-artblock/src/util.cpp @@ -0,0 +1,22 @@ +#include "./include/util.hpp" +#include +using namespace util; +void sendEscapeKeyToWindow(HWND hwnd) { + { + SetForegroundWindow(hwnd); + INPUT inputs[2] = {}; + ZeroMemory(inputs, sizeof(inputs)); + + inputs[0].type = INPUT_KEYBOARD; + inputs[0].ki.wVk = VK_ESCAPE; + + inputs[1].type = INPUT_KEYBOARD; + inputs[1].ki.wVk = VK_ESCAPE; + inputs[1].ki.dwFlags = KEYEVENTF_KEYUP; + + UINT uSent = SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT)); + + debug::out << "SendInput: " << uSent << std::endl; + + } +} \ No newline at end of file