diff --git a/CMakeLists.txt b/CMakeLists.txt index ec0769e..d96662a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,10 +67,6 @@ elseif (EMSCRIPTEN) ) else() # NOT (ANDROID OR EMSCRIPTEN) - if (NOT NINTENDO_SWITCH) - option(ENABLE_DEBUGGER "Enable built-in debugger" ON) - endif() - add_executable(system3) target_link_libraries(system3 PRIVATE ymfm) @@ -117,6 +113,13 @@ else() # NOT (ANDROID OR EMSCRIPTEN) pkg_check_modules(SDL2 REQUIRED IMPORTED_TARGET sdl2) pkg_check_modules(SDL2TTF REQUIRED IMPORTED_TARGET SDL2_ttf) target_link_libraries(system3 PRIVATE PkgConfig::SDL2 PkgConfig::SDL2TTF) + + find_package(nlohmann_json 3.2.0) + if (nlohmann_json_FOUND) + set(ENABLE_DEBUGGER ON) + target_link_libraries(system3 PRIVATE nlohmann_json::nlohmann_json) + endif() + pkg_check_modules(RTMIDI IMPORTED_TARGET rtmidi) if (RTMIDI_FOUND) target_compile_definitions(system3 PRIVATE USE_MIDI) @@ -186,6 +189,7 @@ if (ENABLE_DEBUGGER) src/debugger/debug_info.cpp src/debugger/debugger.cpp src/debugger/cli_frontend.cpp + src/debugger/dap_frontend.cpp ) endif() diff --git a/src/common.h b/src/common.h index b1ee275..96324fe 100644 --- a/src/common.h +++ b/src/common.h @@ -55,6 +55,11 @@ inline void strcpy_s(char* dst, size_t n, const char* src) #define strcasecmp _stricmp #endif +extern uint32_t sdl_custom_event_type; +enum CustomEvent { + DEBUGGER_COMMAND, +}; + // resource.cpp SDL_RWops* open_resource(const char* name, const char* type); SDL_RWops* open_file(const char* name); diff --git a/src/config.cpp b/src/config.cpp index 9c66c71..cccdaa1 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -132,6 +132,8 @@ Config::Config(int argc, char *argv[]) for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-gamedir") == 0) ++i; + else if (strcmp(argv[i], "-version") == 0) + print_version = true; else if (strcmp(argv[i], "-noantialias") == 0) no_antialias = true; else if (strcmp(argv[i], "-savedir") == 0) diff --git a/src/config.h b/src/config.h index 7a0c617..315bdfe 100644 --- a/src/config.h +++ b/src/config.h @@ -41,6 +41,7 @@ struct Config { std::string playlist; std::string title; int midi_device = -1; + bool print_version = false; bool use_fm = false; bool no_antialias = false; bool scanline = false; diff --git a/src/debugger/cli_frontend.cpp b/src/debugger/cli_frontend.cpp index 4ee799d..bb0c2de 100644 --- a/src/debugger/cli_frontend.cpp +++ b/src/debugger/cli_frontend.cpp @@ -42,6 +42,8 @@ public: ~CliFrontend() override = default; + void init() override {} + void repl(int bp_no) override { if (backend->get_state() == State::STOPPED_BREAKPOINT && bp_no) printf("Breakpoint %d\n", bp_no); @@ -67,6 +69,9 @@ public: } } } + + void on_command(void* data) override {} + void on_sleep() override { if (backend->get_state() == State::STOPPED_INTERRUPT) backend->repl(0); @@ -75,7 +80,8 @@ public: void on_palette_change() override { } - void console_output(int lv, const char *output) override { + bool console_output(const char* format, va_list ap) override { + return false; } private: diff --git a/src/debugger/dap_frontend.cpp b/src/debugger/dap_frontend.cpp new file mode 100644 index 0000000..cf8f46e --- /dev/null +++ b/src/debugger/dap_frontend.cpp @@ -0,0 +1,512 @@ +#define JSON_USE_IMPLICIT_CONVERSIONS 0 + +#include "debugger/frontend.h" +#include +#include +#include +#include "nlohmann/json.hpp" +#include "common.h" +#include "encoding.h" +#include "nact.h" +#include "debugger/debugger.h" +using Json = nlohmann::json; + +namespace debugger { + +namespace { + +enum VariablesReference { + VREF_GLOBALS = 1, + VREF_STRINGS, +}; + +void post_debugger_command(void *data) { + SDL_Event event = {}; + event.user.type = sdl_custom_event_type; + event.user.code = DEBUGGER_COMMAND; + event.user.data1 = data; + SDL_PushEvent(&event); +} + +int read_command_thread(void*) { + int content_length = -1; + char header[512]; + while (fgets(header, sizeof(header), stdin)) { + if (sscanf(header, "Content-Length: %d", &content_length) == 1) { + continue; + } else if ((header[0] == '\r' && header[1] == '\n') || header[0] == '\n') { + if (content_length < 0) { + fprintf(stderr, "Debug Adapter Protocol error: no Content-Length header\n"); + continue; + } + char* buf = (char*)malloc(content_length + 1); + fread(buf, content_length, 1, stdin); + buf[content_length] = '\0'; + post_debugger_command(buf); + content_length = -1; + } else { + fprintf(stderr, "Unknown Debug Adapter Protocol header: %s", header); + } + } + post_debugger_command(NULL); // end of messages + return 0; +} + +} // namespace + +class DapFrontend : public Frontend { +public: + DapFrontend(Debugger* backend, const DebugInfo& symbols) : Frontend(backend, symbols) { + SDL_CreateThread(read_command_thread, "Debugger", NULL); + } + + void init() override { + while (!initialized && !g_nact->is_terminating()) { + g_nact->process_next_event(); + while (!queue.empty()) { + char* msg = queue.front(); + queue.pop(); + if (!msg) + return; + handle_message(msg); + } + } + } + + void repl(int bp_no) override { + emit_stopped_event(); + backend->set_state(State::RUNNING); + + bool continue_repl = true; + while (continue_repl && !g_nact->is_terminating()) { + g_nact->process_next_event(); + while (!queue.empty()) { + char* msg = queue.front(); + queue.pop(); + if (!msg) + return; + continue_repl = handle_message(msg); + } + } + } + + void on_command(void* data) override { + queue.push(static_cast(data)); + } + + void on_sleep() override { + while (!queue.empty()) { + char* msg = queue.front(); + queue.pop(); + if (!msg) + return; + handle_message(msg); + } + if (backend->get_state() == State::STOPPED_INTERRUPT || backend->get_state() == State::STOPPED_EXCEPTION) + backend->repl(0); + } + + void on_palette_change() override { + } + + bool console_output(const char* format, va_list ap) override { + return true; + } + +private: + Json create_source(const char *name) { + Json source; + source["name"] = name; + if (!src_dir.empty()) + source["path"] = src_dir + "/" + name; + source["sourceReference"] = 0; + return source; + } + + std::string format_string_value(const char* str) { + char *utf = g_nact->encoding->toUtf8(str); + std::string value = "\""; + value += utf; + value += "\""; + free(utf); + return value; + } + + void send_json(Json& json) { + json["seq"] = ++seq_; + std::string str = json.dump(); + printf("Content-Length: %zu\r\n\r\n%s", str.size(), str.c_str()); + fflush(stdout); + } + + void emit_initialized_event() { + Json json = { + {"type", "event"}, + {"event", "initialized"}, + }; + send_json(json); + } + + void emit_stopped_event() { + std::string reason; + switch (backend->get_state()) { + case State::STOPPED_ENTRY: reason = "entry"; break; + case State::STOPPED_STEP: reason = "step"; break; + case State::STOPPED_NEXT: reason = "step"; break; + case State::STOPPED_BREAKPOINT: reason = "breakpoint"; break; + case State::STOPPED_INTERRUPT: reason = "pause"; break; + case State::STOPPED_EXCEPTION: reason = "exception"; break; + default: reason = "unknown"; break; + } + Json json = { + {"type", "event"}, + {"event", "stopped"}, + {"body", { + {"reason", reason}, + {"allThreadsStopped", true}, + }} + }; + send_json(json); + } + + bool handle_message(char* msg) { + Json json = Json::parse(msg); + auto type = json["type"].get(); + bool continue_repl = true; + if (type == "request") + continue_repl = handle_request(json); + free(msg); + return continue_repl; + } + + bool handle_request(Json& request) { + Json resp; + resp["type"] = "response"; + resp["request_seq"] = request["seq"]; + resp["command"] = request["command"]; + auto command = request["command"].get(); + Json& args = request["arguments"]; + + bool continue_repl = true; + if (command == "initialize") { + cmd_initialize(args, resp); + } else if (command == "disconnect") { + cmd_disconnect(args, resp); + continue_repl = false; + } else if (command == "launch") { + cmd_launch(args, resp); + } else if (command == "configurationDone") { + cmd_configurationDone(args, resp); + } else if (command == "threads") { + cmd_threads(args, resp); + } else if (command == "scopes") { + cmd_scopes(args, resp); + } else if (command == "variables") { + cmd_variables(args, resp); + } else if (command == "setVariable") { + cmd_setVariable(args, resp); + } else if (command == "stackTrace") { + cmd_stackTrace(args, resp); + } else if (command == "evaluate") { + cmd_evaluate(args, resp); + } else if (command == "setBreakpoints") { + cmd_setBreakpoints(args, resp); + } else if (command == "continue") { + cmd_continue(args, resp); + continue_repl = false; + } else if (command == "pause") { + cmd_pause(args, resp); + } else if (command == "stepIn") { + cmd_stepIn(args, resp); + continue_repl = false; + } else if (command == "stepOut") { + cmd_stepOut(args, resp); + continue_repl = false; + } else if (command == "next") { + cmd_next(args, resp); + continue_repl = false; + } else { + fprintf(stderr, "Unknown command '%s'\n", command.c_str()); + resp["success"] = false; + resp["message"] = "Unknown command " + command; + } + send_json(resp); + return continue_repl; + } + + void cmd_initialize(Json& args, Json& resp) { + resp["success"] = true; + resp["body"] = { + {"supportsConfigurationDoneRequest", true}, + {"supportsEvaluateForHovers", true}, + {"supportsSetVariable", true}, + }; + } + + void cmd_disconnect(Json& args, Json& resp) { + resp["success"] = true; + g_nact->quit(0); + } + + void cmd_launch(Json& args, Json& resp) { + if (args["noDebug"].is_boolean() && args["noDebug"].get()) { + resp["success"] = true; + initialized = true; + return; + } + + if (!symbols.loaded()) { + resp["success"] = false; + resp["message"] = "system3: Cannot load debug symbols"; + return; + } + + if (args["srcDir"].is_string()) + src_dir = args["srcDir"].get(); + if (args["stopOnEntry"].is_boolean() && args["stopOnEntry"].get()) + backend->set_state(State::STOPPED_ENTRY); + resp["success"] = true; + + emit_initialized_event(); + } + + void cmd_configurationDone(Json& args, Json& resp) { + initialized = true; + resp["success"] = true; + } + + void cmd_threads(Json& args, Json& resp) { + resp["success"] = true; + resp["body"] = { + {"threads", { + {{"id", 1}, {"name", "main thread"}}, + }} + }; + } + + void cmd_scopes(Json& args, Json& resp) { + resp["success"] = true; + resp["body"] = { + {"scopes", { + { + {"name", "All Variables"}, + {"variablesReference", VREF_GLOBALS}, + {"namedVariables", symbols.num_variables()}, + {"expensive", false} + }, + { + {"name", "Strings"}, + {"variablesReference", VREF_STRINGS}, + {"indexedVariables", MAX_STRVAR}, + {"expensive", false} + }, + }} + }; + } + + void cmd_variables(Json& args, Json& resp) { + const Json& start_ = args["start"]; + const Json& count_ = args["count"]; + int start = start_.is_number() ? start_.get() : 0; + int count = count_.is_number() ? count_.get() : MAX_VAR; + + int var_ref = args["variablesReference"].get(); + if (var_ref == VREF_GLOBALS) { + resp["success"] = true; + Json& variables = resp["body"]["variables"]; + int end = std::min(start + count, static_cast(symbols.num_variables())); + for (int i = start; i < end; i++) { + Json item = { + {"name", symbols.variable_name(i)}, + {"value", std::to_string(g_nact->get_var(i))}, + {"variablesReference", 0}, + }; + variables.push_back(std::move(item)); + } + } else if (var_ref == VREF_STRINGS) { + resp["success"] = true; + Json& variables = resp["body"]["variables"]; + int end = std::min(start + count, MAX_STRVAR); + for (int i = start; i < end; i++) { + char name[10]; + snprintf(name, sizeof(name), "[%d]", i + 1); + Json item = { + {"name", name}, + {"value", format_string_value(g_nact->get_string(i))}, + {"variablesReference", 0}, + }; + variables.push_back(std::move(item)); + } + } else { + resp["success"] = false; + resp["message"] = "Invalid variables reference"; + } + } + + void cmd_setVariable(Json& args, Json& resp) { + switch (args["variablesReference"].get()) { + case VREF_GLOBALS: + { + int var = symbols.lookup_variable(args["name"].get().c_str()); + if (var < 0) { + resp["success"] = false; + resp["message"] = "Invalid variable name"; + return; + } + int parsed_value; + if (sscanf(args["value"].get().c_str(), "%i", &parsed_value) != 1) { + resp["success"] = false; + resp["message"] = "Syntax error"; + return; + } + g_nact->set_var(var, parsed_value); + resp["success"] = true; + resp["body"] = { + {"value", std::to_string(g_nact->get_var(var))}, + }; + } + break; + case VREF_STRINGS: + { + int index; + if (sscanf(args["name"].get().c_str(), "[%d]", &index) != 1 || index <= 0 || index > MAX_STRVAR) { + resp["success"] = false; + resp["message"] = "Invalid string index"; + return; + } + index--; // 1-based to 0-based + + std::string value = args["value"].get(); + if (value.size() < 2 || value[0] != '"' || value.back() != '"') { + resp["success"] = false; + resp["message"] = "Syntax error"; + return; + } + value = value.substr(1, value.size() - 2); + + char* encoded = g_nact->encoding->fromUtf8(value.c_str()); + g_nact->set_string(index, encoded); + free(encoded); + + resp["success"] = true; + resp["body"] = { + {"value", format_string_value(g_nact->get_string(index))}, + }; + } + break; + default: + resp["success"] = false; + resp["message"] = "Invalid variables reference"; + break; + } + } + + void cmd_stackTrace(Json& args, Json& resp) { + resp["success"] = true; + Json& body = resp["body"]; + Json& stackFrames = body["stackFrames"]; + int i = 0; + for (const StackFrame &frame : backend->stack_trace()) { + Json& item = stackFrames[i++]; + item["id"] = i; + item["name"] = frame.src; + item["source"] = create_source(frame.src); + item["line"] = frame.line; + item["column"] = 0; + } + body["totalFrames"] = i; + } + + void cmd_evaluate(Json& args, Json& resp) { + int var = symbols.lookup_variable(args["expression"].get().c_str()); + if (var < 0) { + resp["success"] = false; + resp["message"] = "Invalid expression"; + return; + } + resp["success"] = true; + resp["body"] = { + {"result", std::to_string(g_nact->get_var(var))}, + {"variablesReference", 0}, + }; + } + + void cmd_setBreakpoints(Json& args, Json& resp) { + const auto filename = args["source"]["name"].get(); + int page = symbols.src2page(filename.c_str()); + + backend->delete_breakpoints_in_page(page); + + resp["success"] = true; + Json& body = resp["body"]; + Json& out_bps = body["breakpoints"]; + + for (const Json& srcbp : args["breakpoints"]) { + Json& item = out_bps.emplace_back(); + + int line = srcbp["line"].get(); + int addr = symbols.line2addr(page, line); + if (page < 0) { + item["verified"] = false; + item["message"] = "no source file named " + filename; + continue; + } + if (addr < 0) { + item["verified"] = false; + item["message"] = "no line " + std::to_string(line) + " in file " + filename; + continue; + } + int bp_no = backend->set_breakpoint(page, addr, false); + if (bp_no < 0) { + char message[256]; + snprintf(message, sizeof(message), "failed to set breakpoint at %d:0x%x", page, addr); + item["verified"] = false; + item["message"] = message; + continue; + } + + line = symbols.addr2line(page, addr); + item["id"] = bp_no; + item["verified"] = true; + item["source"] = create_source(filename.c_str()); + item["line"] = line; + } + } + + void cmd_continue(Json& args, Json& resp) { + // TODO: sdl_raiseWindow(); + resp["success"] = true; + } + + void cmd_pause(Json& args, Json& resp) { + backend->set_state(State::STOPPED_INTERRUPT); + resp["success"] = true; + } + + void cmd_stepIn(Json& args, Json& resp) { + backend->stepin(); + resp["success"] = true; + } + + void cmd_stepOut(Json& args, Json& resp) { + backend->stepout(); + resp["success"] = true; + } + + void cmd_next(Json& args, Json& resp) { + backend->next(); + resp["success"] = true; + } + + std::queue queue; + bool initialized = false; + int seq_ = 0; + std::string src_dir; +}; + +// static +Frontend* Frontend::create_dap(Debugger* backend, const DebugInfo& symbols) { + return new DapFrontend(backend, symbols); +} + +} // namespace debugger diff --git a/src/debugger/debugger.cpp b/src/debugger/debugger.cpp index 121cdfd..758af8c 100644 --- a/src/debugger/debugger.cpp +++ b/src/debugger/debugger.cpp @@ -65,7 +65,7 @@ int get_retaddr_if_funcall() { Debugger::Debugger(const char *symbols_path, DebuggerMode mode) { symbols.load(symbols_path); if (mode == DebuggerMode::DAP) { - // frontend = std::make_unique(this); + frontend = std::unique_ptr(Frontend::create_dap(this, symbols)); } else { frontend = std::unique_ptr(Frontend::create_cli(this, symbols)); } @@ -73,6 +73,10 @@ Debugger::Debugger(const char *symbols_path, DebuggerMode mode) { Debugger::~Debugger() = default; +void Debugger::init() { + frontend->init(); +} + void Debugger::repl(int bp_no) { delete_breakpoint(INTERNAL_BREAKPOINT_NO); @@ -114,10 +118,12 @@ uint8_t Debugger::handle_breakpoint(int page, int addr) { return restore_op; } -bool Debugger::console_vprintf(int lv, const char *format, va_list ap) { - return false; +bool Debugger::console_vprintf(const char *format, va_list ap) { + return frontend->console_output(format, ap); } + void Debugger::post_command(void *data) { + frontend->on_command(data); } int Debugger::set_breakpoint(int page, int addr, bool is_internal) diff --git a/src/debugger/debugger.h b/src/debugger/debugger.h index b2749f6..624a493 100644 --- a/src/debugger/debugger.h +++ b/src/debugger/debugger.h @@ -41,11 +41,12 @@ public: void repl(int bp_no); // API for VM + void init(); bool trapped() const { return state != State::RUNNING; } void on_sleep(); void on_palette_change(); uint8_t handle_breakpoint(int page, int addr); - bool console_vprintf(int lv, const char *format, va_list ap); + bool console_vprintf(const char *format, va_list ap); void post_command(void *data); // API for frontend diff --git a/src/debugger/frontend.h b/src/debugger/frontend.h index f441349..f5efb16 100644 --- a/src/debugger/frontend.h +++ b/src/debugger/frontend.h @@ -1,6 +1,8 @@ #ifndef _DEBUGGER_FRONTEND_H_ #define _DEBUGGER_FRONTEND_H_ +#include + namespace debugger { class Debugger; @@ -9,13 +11,16 @@ class DebugInfo; class Frontend { public: static Frontend* create_cli(Debugger* backend, const DebugInfo& symbols); + static Frontend* create_dap(Debugger* backend, const DebugInfo& symbols); Frontend(Debugger* backend, const DebugInfo& symbols) : backend(backend), symbols(symbols) {} virtual ~Frontend() = default; + virtual void init() = 0; virtual void repl(int bp_no) = 0; + virtual void on_command(void* data) = 0; virtual void on_sleep() = 0; virtual void on_palette_change() = 0; - virtual void console_output(int lv, const char *output) = 0; + virtual bool console_output(const char* format, va_list ap) = 0; protected: Debugger* backend; const DebugInfo& symbols; diff --git a/src/generic/nact_generic.cpp b/src/generic/nact_generic.cpp index eb86efa..340c66a 100644 --- a/src/generic/nact_generic.cpp +++ b/src/generic/nact_generic.cpp @@ -1,5 +1,6 @@ #include #include "nact.h" +#include "debugger/debugger.h" void NACT::text_dialog() { @@ -15,6 +16,16 @@ void NACT::platform_finalize() void NACT::output_console(const char *format, ...) { +#ifdef ENABLE_DEBUGGER + if (g_debugger) { + va_list ap; + va_start(ap, format); + bool handled = g_debugger->console_vprintf(format, ap); + va_end(ap); + if (handled) + return; + } +#endif #if defined(_DEBUG_CONSOLE) va_list ap; diff --git a/src/sdlmain.cpp b/src/sdlmain.cpp index 0e0a14b..e278be8 100644 --- a/src/sdlmain.cpp +++ b/src/sdlmain.cpp @@ -69,16 +69,23 @@ SDL_Window* create_window(const GameId& game_id) } // namespace +uint32_t sdl_custom_event_type; + int main(int argc, char *argv[]) { #ifdef __SWITCH__ romfsInit(); #endif Config config(argc, argv); + if (config.print_version) { + puts(SYSTEM3_VERSION); + return 0; + } GameId game_id(config); g_window = create_window(game_id); g_renderer = SDL_CreateRenderer(g_window, -1, 0); + sdl_custom_event_type = SDL_RegisterEvents(1); // system3 初期化 g_nact.reset(NACT::create(config, game_id)); @@ -112,6 +119,7 @@ int main(int argc, char *argv[]) #ifdef ENABLE_DEBUGGER if (config.debugger_mode != DebuggerMode::DISABLED) { g_debugger = std::make_unique("ADISK.DAT.symbols", config.debugger_mode); + g_debugger->init(); } #endif diff --git a/src/sys/nact.cpp b/src/sys/nact.cpp index 5542d09..3e9b9e9 100644 --- a/src/sys/nact.cpp +++ b/src/sys/nact.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "nact.h" #include "encoding.h" #include "ags.h" @@ -843,6 +844,23 @@ void NACT::sys_sleep(int ms) { #endif } +void NACT::set_string(int index, const char* value) +{ + const char *src = value; + char *dst = tvar[index]; + int remaining = sizeof(tvar[0]) - 1; + while (*src) { + int len = encoding->mblen(*src); + if (len > remaining) + break; + memcpy(dst, src, len); + src += len; + dst += len; + remaining -= len; + } + *dst = '\0'; +} + // WinMainとのインターフェース int NACT::get_screen_height() diff --git a/src/sys/nact.h b/src/sys/nact.h index d04c67a..f1ec3a5 100644 --- a/src/sys/nact.h +++ b/src/sys/nact.h @@ -80,6 +80,7 @@ #define MAX_VERB 128 #define MAX_OBJ 256 #define MAX_PCM 256 +#define MAX_VAR 512 #define MAX_STRVAR 10 class AGS; @@ -109,7 +110,7 @@ protected: void execute(); // 変数 - uint16 var[512] = {}; + uint16 var[MAX_VAR] = {}; uint16 var_stack[30][20] = {}; char tvar[MAX_STRVAR][33] = {}; char tvar_stack[30][MAX_STRVAR][22] = {}; @@ -229,6 +230,8 @@ public: int mainloop(); void sys_sleep(int ms); void quit(int code); + void process_next_event(); + bool is_terminating() const { return terminate; } int get_screen_height(); @@ -250,10 +253,13 @@ public: int get_scenario_page() const { return sco.page(); } uint16 get_var(int index) const { return var[index]; } + void set_var(int index, uint16_t value) { var[index] = value; } const char* get_string(int index) const { return tvar[index]; } + void set_string(int index, const char* value); private: void pump_events(); + void handle_event(SDL_Event e); bool handle_platform_event(const SDL_Event& e); }; diff --git a/src/sys/nact_input.cpp b/src/sys/nact_input.cpp index 82a7bf9..c8e4fb9 100644 --- a/src/sys/nact_input.cpp +++ b/src/sys/nact_input.cpp @@ -12,6 +12,7 @@ #include "nact.h" #include "ags.h" #include "texthook.h" +#include "debugger/debugger.h" enum TouchState { TOUCH_NONE, @@ -23,65 +24,89 @@ extern SDL_Window* g_window; static int mousex, mousey; static TouchState touch_state = TOUCH_NONE; +void NACT::handle_event(SDL_Event e) +{ + if (handle_platform_event(e)) + return; + + switch (e.type) { + case SDL_QUIT: + quit(0); + break; + + case SDL_WINDOWEVENT: + switch (e.window.event) { + case SDL_WINDOWEVENT_EXPOSED: + case SDL_WINDOWEVENT_SIZE_CHANGED: + ags->flush_screen(false); + break; + } + break; + + case SDL_MOUSEMOTION: + mousex = e.motion.x * ags->screen_width / ags->window_width; + mousey = e.motion.y * ags->screen_height / ags->window_height; + break; + + case SDL_FINGERDOWN: + case SDL_FINGERUP: + case SDL_FINGERMOTION: + mousex = e.tfinger.x * ags->screen_width; + mousey = e.tfinger.y * ags->screen_height; + switch (SDL_GetNumTouchFingers(e.tfinger.touchId)) { + case 0: + touch_state = TOUCH_NONE; + break; + case 1: + // A touch outside of the viewport (SDL clamps it to 0.0-1.0) is + // a right-click. + if (e.tfinger.x == 0.0f || e.tfinger.x == 1.0f || + e.tfinger.y == 0.0f || e.tfinger.y == 1.0f) { + touch_state = TOUCH_RBUTTON; + } else { + touch_state = TOUCH_LBUTTON; + } + break; + case 2: + // Two-finger touch is a right-click. + touch_state = TOUCH_RBUTTON; + break; + } + break; + + case SDL_APP_DIDENTERFOREGROUND: + ags->flush_screen(false); + break; + + default: +#ifdef ENABLE_DEBUGGER + if (e.type == sdl_custom_event_type) { + switch (e.user.code) { + case DEBUGGER_COMMAND: + g_debugger->post_command(e.user.data1); + break; + } + } +#endif + break; + } +} + void NACT::pump_events() { SDL_Event e; while (SDL_PollEvent(&e)) { - if (handle_platform_event(e)) - continue; - - switch (e.type) { - case SDL_QUIT: - quit(0); - break; - - case SDL_WINDOWEVENT: - switch (e.window.event) { - case SDL_WINDOWEVENT_EXPOSED: - case SDL_WINDOWEVENT_SIZE_CHANGED: - ags->flush_screen(false); - break; - } - break; - - case SDL_MOUSEMOTION: - mousex = e.motion.x * ags->screen_width / ags->window_width; - mousey = e.motion.y * ags->screen_height / ags->window_height; - break; - - case SDL_FINGERDOWN: - case SDL_FINGERUP: - case SDL_FINGERMOTION: - mousex = e.tfinger.x * ags->screen_width; - mousey = e.tfinger.y * ags->screen_height; - switch (SDL_GetNumTouchFingers(e.tfinger.touchId)) { - case 0: - touch_state = TOUCH_NONE; - break; - case 1: - // A touch outside of the viewport (SDL clamps it to 0.0-1.0) is - // a right-click. - if (e.tfinger.x == 0.0f || e.tfinger.x == 1.0f || - e.tfinger.y == 0.0f || e.tfinger.y == 1.0f) { - touch_state = TOUCH_RBUTTON; - } else { - touch_state = TOUCH_LBUTTON; - } - break; - case 2: - // Two-finger touch is a right-click. - touch_state = TOUCH_RBUTTON; - break; - } - break; - - case SDL_APP_DIDENTERFOREGROUND: - ags->flush_screen(false); - break; - } + handle_event(std::move(e)); } } +void NACT::process_next_event() +{ + SDL_Event e; + SDL_WaitEvent(&e); + handle_event(std::move(e)); +} + uint8 NACT::get_key() { uint8 val = 0; diff --git a/src/win/nact_win.cpp b/src/win/nact_win.cpp index 76c9961..3f479f9 100644 --- a/src/win/nact_win.cpp +++ b/src/win/nact_win.cpp @@ -10,6 +10,7 @@ #include "msgskip.h" #include "texthook.h" #include "resource.h" +#include "debugger/debugger.h" extern SDL_Window* g_window; @@ -150,6 +151,16 @@ void NACT::platform_finalize() void NACT::output_console(const char *format, ...) { +#ifdef ENABLE_DEBUGGER + if (g_debugger) { + va_list ap; + va_start(ap, format); + bool handled = g_debugger->console_vprintf(format, ap); + va_end(ap); + if (handled) + return; + } +#endif #if defined(_DEBUG_CONSOLE) va_list ap;