Add support for Debug Adapter Protocol

This commit is contained in:
kichikuou
2024-12-28 14:09:56 +09:00
parent 8883bd5d68
commit 909140f3bc
15 changed files with 684 additions and 63 deletions
+8 -4
View File
@@ -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()
+5
View File
@@ -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);
+2
View File
@@ -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)
+1
View File
@@ -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;
+7 -1
View File
@@ -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:
+512
View File
@@ -0,0 +1,512 @@
#define JSON_USE_IMPLICIT_CONVERSIONS 0
#include "debugger/frontend.h"
#include <algorithm>
#include <queue>
#include <SDL.h>
#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<char*>(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<std::string>();
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<std::string>();
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<bool>()) {
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<std::string>();
if (args["stopOnEntry"].is_boolean() && args["stopOnEntry"].get<bool>())
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<int>() : 0;
int count = count_.is_number() ? count_.get<int>() : MAX_VAR;
int var_ref = args["variablesReference"].get<int>();
if (var_ref == VREF_GLOBALS) {
resp["success"] = true;
Json& variables = resp["body"]["variables"];
int end = std::min(start + count, static_cast<int>(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<int>()) {
case VREF_GLOBALS:
{
int var = symbols.lookup_variable(args["name"].get<std::string>().c_str());
if (var < 0) {
resp["success"] = false;
resp["message"] = "Invalid variable name";
return;
}
int parsed_value;
if (sscanf(args["value"].get<std::string>().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<std::string>().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<std::string>();
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<std::string>().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<std::string>();
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>();
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<char*> 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
+9 -3
View File
@@ -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<DapFrontend>(this);
frontend = std::unique_ptr<Frontend>(Frontend::create_dap(this, symbols));
} else {
frontend = std::unique_ptr<Frontend>(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)
+2 -1
View File
@@ -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
+6 -1
View File
@@ -1,6 +1,8 @@
#ifndef _DEBUGGER_FRONTEND_H_
#define _DEBUGGER_FRONTEND_H_
#include <stdarg.h>
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;
+11
View File
@@ -1,5 +1,6 @@
#include <stdio.h>
#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;
+8
View File
@@ -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<debugger::Debugger>("ADISK.DAT.symbols", config.debugger_mode);
g_debugger->init();
}
#endif
+18
View File
@@ -6,6 +6,7 @@
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#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()
+7 -1
View File
@@ -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);
};
+77 -52
View File
@@ -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;
+11
View File
@@ -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;