diff --git a/include/core/render.hpp b/include/core/render.hpp index bfb6521d..08461c9a 100644 --- a/include/core/render.hpp +++ b/include/core/render.hpp @@ -386,7 +386,6 @@ private: std::map memory; }; - /** * Clip range inside another. * diff --git a/include/platform/sdl/platform.hpp b/include/platform/sdl/platform.hpp index 73010a10..0fc32806 100644 --- a/include/platform/sdl/platform.hpp +++ b/include/platform/sdl/platform.hpp @@ -38,6 +38,12 @@ #define SPECIAL_LEFT 0x4000000CUL //Right. #define SPECIAL_RIGHT 0x4000000DUL +//Left word. +#define SPECIAL_LEFT_WORD 0x4000000EUL +//Right word. +#define SPECIAL_RIGHT_WORD 0x4000000FUL +//Delete word. +#define SPECIAL_DELETE_WORD 0x40000010UL //Pressed mask. #define PRESSED_MASK 0x80000000UL @@ -193,6 +199,12 @@ struct commandline_model * Enable command line. */ void enable() throw(); +/** + * Enable command line with specific command. + * + * Parameter cmd: The command template. + */ + void enable(const std::string& cmd) throw(); /** * Repaint to SDL surface. * @@ -421,6 +433,8 @@ void notify_emulator_exit(); * The user interface loop. Call in UI thread. notify_emulator_exit() causes this to return. */ void ui_loop(); - - +/** + * Save the config. + */ +void lsnes_sdl_save_config(); #endif diff --git a/manual.lyx b/manual.lyx index 9e1a3319..16fbf418 100644 --- a/manual.lyx +++ b/manual.lyx @@ -1565,6 +1565,14 @@ scroll-down Scroll messages window forward one screenful. \end_layout +\begin_layout Subsubsection +prompt-command +\end_layout + +\begin_layout Standard +Enter command prompt, with prompt prepopulated with specified command. +\end_layout + \begin_layout Section Settings \end_layout diff --git a/manual.txt b/manual.txt index 3eda44f6..37c69742 100644 --- a/manual.txt +++ b/manual.txt @@ -765,6 +765,11 @@ Scroll messages window back one screenful. Scroll messages window forward one screenful. +6.12.7 prompt-command + +Enter command prompt, with prompt prepopulated with specified +command. + 7 Settings 7.1 Core settings diff --git a/src/platform/sdl/Makefile b/src/platform/sdl/Makefile index df7d6279..41a054d7 100644 --- a/src/platform/sdl/Makefile +++ b/src/platform/sdl/Makefile @@ -1,5 +1,5 @@ ifeq ($(GRAPHICS), SDL) -OBJECTS = commandline.$(OBJECT_SUFFIX) drawprim.$(OBJECT_SUFFIX) graphicsfn.$(OBJECT_SUFFIX) keyboard.$(OBJECT_SUFFIX) main.$(OBJECT_SUFFIX) thread.$(OBJECT_SUFFIX) status.$(OBJECT_SUFFIX) +OBJECTS = commandline.$(OBJECT_SUFFIX) drawprim.$(OBJECT_SUFFIX) graphicsfn.$(OBJECT_SUFFIX) keyboard.$(OBJECT_SUFFIX) main.$(OBJECT_SUFFIX) thread.$(OBJECT_SUFFIX) status.$(OBJECT_SUFFIX) savesettings.$(OBJECT_SUFFIX) SDL_CFLAGS += $(shell $(CROSS_PREFIX)sdl-config --cflags) SDL_LDFLAGS += $(shell $(CROSS_PREFIX)sdl-config --libs) else diff --git a/src/platform/sdl/commandline.cpp b/src/platform/sdl/commandline.cpp index 81e46f70..aea101cd 100644 --- a/src/platform/sdl/commandline.cpp +++ b/src/platform/sdl/commandline.cpp @@ -7,6 +7,34 @@ namespace { volatile uint32_t autorepeat_first = 10; volatile uint32_t autorepeat_subsequent = 4; + + bool is_whitespace(uint32_t cp) + { + switch(cp) { + case 12: + case 32: + case 9: + case 0x1680: + case 0x180E: + case 0x2000: + case 0x2001: + case 0x2002: + case 0x2003: + case 0x2004: + case 0x2005: + case 0x2006: + case 0x2007: + case 0x2008: + case 0x2009: + case 0x200A: + case 0x2028: + case 0x205F: + case 0x3000: + return true; + default: + return false; + }; + } } commandline_model::commandline_model() throw() @@ -91,6 +119,31 @@ std::string commandline_model::key(uint32_t k) throw(std::bad_alloc) return ""; delete_codepoint(cursor_pos); return ""; + case SPECIAL_LEFT_WORD: + while(cursor_pos > 0 && (cursor_pos == codepoints.size() || isspace(codepoints[cursor_pos]))) + cursor_pos--; + while(cursor_pos > 0 && (cursor_pos == codepoints.size() || !isspace(codepoints[cursor_pos]))) + cursor_pos--; + //If the previous position is whitespace, back off to it. + while(cursor_pos > 0 && isspace(codepoints[cursor_pos - 1])) + cursor_pos--; + return ""; + case SPECIAL_RIGHT_WORD: + while(cursor_pos < codepoints.size() && isspace(codepoints[cursor_pos])) + cursor_pos++; + while(cursor_pos < codepoints.size() && !isspace(codepoints[cursor_pos])) + cursor_pos++; + return ""; + case SPECIAL_DELETE_WORD: + while(cursor_pos > 0 && !isspace(codepoints[cursor_pos - 1])) { + delete_codepoint(cursor_pos - 1); + cursor_pos--; + } + while(cursor_pos > 0 && isspace(codepoints[cursor_pos - 1])) { + delete_codepoint(cursor_pos - 1); + cursor_pos--; + } + return ""; case 0: case PRESSED_MASK: //Eh? @@ -145,6 +198,35 @@ bool commandline_model::overwriting() throw() return overwrite_mode; } +void commandline_model::enable(const std::string& cmd) throw() +{ + enable(); + unsigned left = 0; + unsigned tmp = 0; + for(size_t itr = 0; itr < cmd.length(); itr++) { + unsigned char ch = cmd[itr]; + if(ch < 128) + codepoints.push_back(ch); + else if(left) { + left--; + tmp = tmp * 64 + (ch & 0x3F); + if(!left) + codepoints.push_back(tmp); + } else if(ch < 192) { + } else if(ch < 224) { + left = 1; + tmp = ch & 0x1F; + } else if(ch < 240) { + left = 2; + tmp = ch & 0x0F; + } else if(ch < 248) { + left = 3; + tmp = ch & 0x07; + } + } + cursor_pos = codepoints.size(); +} + void commandline_model::enable() throw() { if(enabled_flag) diff --git a/src/platform/sdl/graphicsfn.cpp b/src/platform/sdl/graphicsfn.cpp index cdc4b8ed..5a6d193a 100644 --- a/src/platform/sdl/graphicsfn.cpp +++ b/src/platform/sdl/graphicsfn.cpp @@ -69,6 +69,9 @@ namespace SDL_TimerID timer_id; //Timer IRQ counter. Used by identify key stuff. volatile unsigned timer_irq_counter; + //Delayed command to start. + bool delayed_cmd_active; + std::string delayed_cmd; void sigalrm_handler(int s) { @@ -270,6 +273,13 @@ namespace platform::queue(emu_handle_identify, NULL, false); } + function_ptr_command exec_command_prefix("prompt-command", + "Prompt for command", "Syntax: prompt-command \nPrompts command with specified text.\n", + [](const std::string& line) throw(std::bad_alloc, std::runtime_error) { + delayed_cmd = line; + delayed_cmd_active = true; + }); + //Handle QUIT in normal state. void ui_handle_quit_signal() { @@ -307,7 +317,7 @@ void ui_loop() { ui_mutex->lock(); int r = SDL_PollEvent(&e); - if(!repaint_in_flight && !timer_triggered && !r) { + if(!repaint_in_flight && !timer_triggered && !delayed_cmd_active && !r) { ui_mutex->unlock(); usleep(2000); continue; @@ -386,9 +396,13 @@ void ui_loop() switch(special_mode) { case SPECIALMODE_NORMAL: //Enable command line if needed. - if(iskbd && kbdkey == SDLK_ESCAPE) { + if((iskbd && kbdkey == SDLK_ESCAPE) || delayed_cmd_active) { if(!cmdline.enabled() && !polarity) { - cmdline.enable(); + if(delayed_cmd_active) + cmdline.enable(delayed_cmd); + else + cmdline.enable(); + delayed_cmd_active = false; commandline_updated = true; special_mode = SPECIALMODE_COMMAND; platform::set_modal_pause(true); diff --git a/src/platform/sdl/keyboard.cpp b/src/platform/sdl/keyboard.cpp index 60347772..4d6bfb38 100644 --- a/src/platform/sdl/keyboard.cpp +++ b/src/platform/sdl/keyboard.cpp @@ -306,6 +306,46 @@ uint32_t get_command_edit_operation(SDL_Event& e, bool enable) //Everything except keyboard is no-op. if(e.type != SDL_KEYDOWN && e.type != SDL_KEYUP) return SPECIAL_NOOP; + //Keys with CTRL held. + if(e.key.keysym.mod & KMOD_CTRL) { + switch(e.key.keysym.sym) { + case 'A': case 'a': + return SPECIAL_HOME | press; + case 'B': case 'b': + return SPECIAL_LEFT | press; + case 'D': case 'd': + return SPECIAL_DELETE | press; + case 'E': case 'e': + return SPECIAL_END | press; + case 'F': case 'f': + return SPECIAL_RIGHT | press; + case 'P': case 'p': + return SPECIAL_UP | press; + case 'N': case 'n': + return SPECIAL_DOWN | press; + case SDLK_LEFT: + return SPECIAL_LEFT_WORD | press; + case SDLK_RIGHT: + return SPECIAL_RIGHT_WORD | press; + case 'W': case 'w': + return SPECIAL_DELETE_WORD | press; + default: + return SPECIAL_NOOP; + }; + } + //Keys with ALT held. + if(e.key.keysym.mod & KMOD_ALT) { + switch(e.key.keysym.sym) { + case 'B': case 'b': + return SPECIAL_LEFT_WORD | press; + case 'D': case 'd': + return SPECIAL_DELETE_WORD | press; + case 'F': case 'f': + return SPECIAL_RIGHT_WORD | press; + default: + return SPECIAL_NOOP; + }; + } //Escape is special. if(e.key.keysym.sym == SDLK_ESCAPE) return (e.type == SDL_KEYUP) ? SPECIAL_NAK : SPECIAL_NOOP; diff --git a/src/platform/sdl/main.cpp b/src/platform/sdl/main.cpp index c6679133..d649bba3 100644 --- a/src/platform/sdl/main.cpp +++ b/src/platform/sdl/main.cpp @@ -210,6 +210,7 @@ int main(int argc, char** argv) fatal_error(); return 1; } + lsnes_sdl_save_config(); rrdata::close(); platform::quit(); quit_lua(); diff --git a/src/platform/sdl/savesettings.cpp b/src/platform/sdl/savesettings.cpp new file mode 100644 index 00000000..4521b955 --- /dev/null +++ b/src/platform/sdl/savesettings.cpp @@ -0,0 +1,94 @@ +#include "core/command.hpp" +#include "core/controller.hpp" +#include "core/dispatch.hpp" +#include "core/framerate.hpp" +#include "lua/lua.hpp" +#include "core/mainloop.hpp" +#include "core/misc.hpp" +#include "core/moviedata.hpp" +#include "core/rom.hpp" +#include "core/rrdata.hpp" +#include "core/settings.hpp" +#include "core/window.hpp" +#include "library/zip.hpp" + + +namespace +{ + void write_configuration(const std::string& cfg) + { + std::ofstream cfgfile(cfg.c_str()); + //Joystick axis. + for(auto i : keygroup::get_axis_set()) { + keygroup* k = keygroup::lookup_by_name(i); + auto p = k->get_parameters(); + cfgfile << "set-axis " << i << " "; + switch(p.ktype) { + case keygroup::KT_DISABLED: cfgfile << "disabled"; break; + case keygroup::KT_AXIS_PAIR: cfgfile << "axis"; break; + case keygroup::KT_AXIS_PAIR_INVERSE: cfgfile << "axis-inverse"; break; + case keygroup::KT_PRESSURE_M0: cfgfile << "pressure-0"; break; + case keygroup::KT_PRESSURE_MP: cfgfile << "pressure-+"; break; + case keygroup::KT_PRESSURE_0M: cfgfile << "pressure0-"; break; + case keygroup::KT_PRESSURE_0P: cfgfile << "pressure0+"; break; + case keygroup::KT_PRESSURE_PM: cfgfile << "pressure+-"; break; + case keygroup::KT_PRESSURE_P0: cfgfile << "pressure+0"; break; + }; + cfgfile << " minus=" << p.cal_left << " zero=" << p.cal_center << " plus=" << p.cal_right + << " tolerance=" << p.cal_tolerance << std::endl; + } + //Settings. + for(auto i : setting::get_settings_set()) { + if(!setting::is_set(i)) + cfgfile << "unset-setting " << i << std::endl; + else + cfgfile << "set-setting " << i << " " << setting::get(i) << std::endl; + } + //Aliases. + for(auto i : command::get_aliases()) { + std::string old_alias_value = command::get_alias_for(i); + while(old_alias_value != "") { + std::string aliasline; + size_t s = old_alias_value.find_first_of("\n"); + if(s < old_alias_value.length()) { + aliasline = old_alias_value.substr(0, s); + old_alias_value = old_alias_value.substr(s + 1); + } else { + aliasline = old_alias_value; + old_alias_value = ""; + } + cfgfile << "alias-command " << i << " " << aliasline << std::endl; + } + } + //Keybindings. + for(auto i : keymapper::get_bindings()) { + std::string i2 = i; + size_t s = i2.find_first_of("|"); + size_t s2 = i2.find_first_of("/"); + if(s > i2.length() || s2 > s) + continue; + std::string key = i2.substr(s + 1); + std::string mod = i2.substr(0, s2); + std::string modspec = i2.substr(s2 + 1, s - s2 - 1); + std::string old_command_value = keymapper::get_command_for(i); + if(mod != "" || modspec != "") + cfgfile << "bind-key " << mod << "/" << modspec << " " << key << " " + << old_command_value << std::endl; + else + cfgfile << "bind-key " << key << " " << old_command_value << std::endl; + } + } +} + +void lsnes_sdl_save_config() +{ + std::string cfg = get_config_path() + "/lsnes.rc"; + std::string cfgn = cfg + ".new"; + write_configuration(cfg + ".new"); +#if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE) + //Grumble, Windows seemingly can't do this atomically. + remove(cfg.c_str()); +#endif + rename(cfgn.c_str(), cfg.c_str()); + +} \ No newline at end of file