Remove obsolete SDL stuff
This commit is contained in:
parent
690c561d2d
commit
7b0b39d536
20 changed files with 61 additions and 4401 deletions
|
@ -1,100 +0,0 @@
|
|||
#ifndef _plat_sdl__paint__hpp__included__
|
||||
#define _plat_sdl__paint__hpp__included__
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
struct sdlw_display_parameters
|
||||
{
|
||||
//Fill this structure.
|
||||
sdlw_display_parameters();
|
||||
sdlw_display_parameters(uint32_t rscrw, uint32_t rscrh, bool cactive);
|
||||
//Real width of screen buffer.
|
||||
uint32_t real_screen_w;
|
||||
//Real height of screen buffer.
|
||||
uint32_t real_screen_h;
|
||||
//Fullscreen console active flag.
|
||||
bool fullscreen_console;
|
||||
//Virtual width of screen area.
|
||||
uint32_t virtual_screen_w;
|
||||
//Virtual height of screen area.
|
||||
uint32_t virtual_screen_h;
|
||||
//Display width.
|
||||
uint32_t display_w;
|
||||
//Display height.
|
||||
uint32_t display_h;
|
||||
//Screen area left edge x.
|
||||
uint32_t screenarea_x;
|
||||
//Screen area top edge y.
|
||||
uint32_t screenarea_y;
|
||||
//Screen area width.
|
||||
uint32_t screenarea_w;
|
||||
//Screen area height.
|
||||
uint32_t screenarea_h;
|
||||
//Status area left edge x.
|
||||
uint32_t statusarea_x;
|
||||
//Status area top edge y.
|
||||
uint32_t statusarea_y;
|
||||
//Status area width.
|
||||
uint32_t statusarea_w;
|
||||
//Status area height.
|
||||
uint32_t statusarea_h;
|
||||
//Status area lines.
|
||||
uint32_t statusarea_lines;
|
||||
//Message area left edge x.
|
||||
uint32_t messagearea_x;
|
||||
//Message area top edge y.
|
||||
uint32_t messagearea_y;
|
||||
//Message area width.
|
||||
uint32_t messagearea_w;
|
||||
//Message area height.
|
||||
uint32_t messagearea_h;
|
||||
//Message area lines.
|
||||
uint32_t messagearea_lines;
|
||||
//Message area trailing blank top edge y.
|
||||
uint32_t messagearea_trailing_y;
|
||||
//Message area trailing blank height.
|
||||
uint32_t messagearea_trailing_h;
|
||||
//Command line left edge x.
|
||||
uint32_t cmdline_x;
|
||||
//Command line top edge y.
|
||||
uint32_t cmdline_y;
|
||||
//Command line width
|
||||
uint32_t cmdline_w;
|
||||
};
|
||||
|
||||
struct command_status
|
||||
{
|
||||
bool active;
|
||||
bool overwrite;
|
||||
uint32_t rawpos;
|
||||
std::string encoded;
|
||||
};
|
||||
|
||||
//Query status of command line.
|
||||
struct command_status get_current_command();
|
||||
|
||||
//Draw outline box. The surface must be locked.
|
||||
void draw_box(SDL_Surface* surf, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t color);
|
||||
//Draw main screen. The surface must be locked.
|
||||
bool paint_screen(SDL_Surface* surf, const sdlw_display_parameters& p, bool full);
|
||||
//Draw status area. The surface must be locked.
|
||||
bool paint_status(SDL_Surface* surf, const sdlw_display_parameters& p, bool full);
|
||||
//Draw messages. The surface must be locked.
|
||||
bool paint_messages(SDL_Surface* surf, const sdlw_display_parameters& p, bool full);
|
||||
//Draw command. The surface must be locked.
|
||||
bool paint_command(SDL_Surface* surf, const sdlw_display_parameters& p, bool full);
|
||||
//Draw a modal dialog. The surface must be locked.
|
||||
void paint_modal_dialog(SDL_Surface* surf, const std::string& text, bool confirm);
|
||||
|
||||
void sdlw_paint_modal_dialog(const std::string& text, bool confirm);
|
||||
void sdlw_clear_modal_dialog();
|
||||
void sdlw_screen_paintable();
|
||||
void sdlw_command_updated();
|
||||
void sdlw_fullscreen_console(bool enable);
|
||||
void sdlw_force_paint();
|
||||
std::string sdlw_decode_string(std::string e);
|
||||
|
||||
#endif
|
|
@ -1,440 +0,0 @@
|
|||
#ifndef _plat_sdl__platform__hpp__included__
|
||||
#define _plat_sdl__platform__hpp__included__
|
||||
|
||||
#include "core/keymapper.hpp"
|
||||
#include "core/window.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <SDL.h>
|
||||
#include <stdexcept>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
//No-op
|
||||
#define SPECIAL_NOOP 0x40000000UL
|
||||
//Acknowledge line.
|
||||
#define SPECIAL_ACK 0x40000001UL
|
||||
//Negative acknowledge line.
|
||||
#define SPECIAL_NAK 0x40000002UL
|
||||
//Backspace.
|
||||
#define SPECIAL_BACKSPACE 0x40000003UL
|
||||
//Insert
|
||||
#define SPECIAL_INSERT 0x40000004UL
|
||||
//Delete
|
||||
#define SPECIAL_DELETE 0x40000005UL
|
||||
//Home
|
||||
#define SPECIAL_HOME 0x40000006UL
|
||||
//End
|
||||
#define SPECIAL_END 0x40000007UL
|
||||
//Page Up.
|
||||
#define SPECIAL_PGUP 0x40000008UL
|
||||
//Page Down.
|
||||
#define SPECIAL_PGDN 0x40000009UL
|
||||
//Up.
|
||||
#define SPECIAL_UP 0x4000000AUL
|
||||
//Down.
|
||||
#define SPECIAL_DOWN 0x4000000BUL
|
||||
//Left.
|
||||
#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
|
||||
|
||||
//Wake UI thread: Timer tick.
|
||||
#define WAKE_UI_TIMER_TICK 0
|
||||
//Wake UI thread: Identify complete.
|
||||
#define WAKE_UI_IDENTIFY_COMPLETE 1
|
||||
//Wake UI thread: Repaint
|
||||
#define WAKE_UI_REPAINT 2
|
||||
|
||||
/**
|
||||
* Parse SDL event into commandline edit operation.
|
||||
*
|
||||
* Parameter e: The SDL event.
|
||||
* Parameter enable: If true, do parsing, otherwise return SPECIAL_NOOP.
|
||||
* Returns: High bit is 1 if key is pressed, else 0. Low 31 bits are unicode key code or some SPECIAL_* value.
|
||||
*/
|
||||
uint32_t get_command_edit_operation(SDL_Event& e, bool enable);
|
||||
|
||||
/**
|
||||
* Init all keyboard keys.
|
||||
*/
|
||||
void init_sdl_keys();
|
||||
|
||||
/**
|
||||
* Deinit all keyboard keys.
|
||||
*/
|
||||
void deinit_sdl_keys();
|
||||
|
||||
/**
|
||||
* Translate SDL keyboard event.
|
||||
*
|
||||
* Parameter e: The event to translate.
|
||||
* Parameter k: The first translation result is written here.
|
||||
* Returns: 0 if event is not valid keyboard event. 1 if k has been filled.
|
||||
*/
|
||||
unsigned translate_sdl_key(SDL_Event& e, keypress& k);
|
||||
|
||||
/**
|
||||
* Translate SDL joystick event.
|
||||
*
|
||||
* Parameter e: The event to translate.
|
||||
* Parameter k: The first translation result is written here.
|
||||
* Returns: 0 if event is not valid joystick event, 1 if k has been filled.
|
||||
*/
|
||||
unsigned translate_sdl_joystick(SDL_Event& e, keypress& k);
|
||||
|
||||
/**
|
||||
* Draw a string to framebuffer.
|
||||
*
|
||||
* Parameter base: The base address of framebuffer.
|
||||
* Parameter pitch: Separation between lines in framebuffer in bytes.
|
||||
* Parameter pbytes: Bytes per pixel.
|
||||
* Parameter s: The string to draw.
|
||||
* Parameter x: The x-coordinate to start drawing from.
|
||||
* Parameter y: The y-coordinate to start drawing from.
|
||||
* Parameter maxwidth: Maximum width to draw. If greater than text width, the remainder is blacked, if less than
|
||||
* text width, the text is truncated.
|
||||
* Parameter color: The raw color to draw the text using.
|
||||
* Parameter hilite_mode: Hilighting mode: 0 => off, 1 => Underline, 2 => Invert
|
||||
* Parameter hilite_pos: The character index to hilight. May be length of string (character after end is hilighted).
|
||||
*/
|
||||
void draw_string(uint8_t* base, uint32_t pitch, uint32_t pbytes, std::string s, uint32_t x, uint32_t y,
|
||||
uint32_t maxwidth, uint32_t color, uint32_t hilite_mode = 0, uint32_t hilite_pos = 0) throw();
|
||||
|
||||
/**
|
||||
* Draw a string to SDL surface.
|
||||
*
|
||||
* Parameter surf: The surface to draw on. Must be locked.
|
||||
* Parameter s: The string to draw.
|
||||
* Parameter x: The x-coordinate to start drawing from.
|
||||
* Parameter y: The y-coordinate to start drawing from.
|
||||
* Parameter maxwidth: Maximum width to draw. If greater than text width, the remainder is blacked, if less than
|
||||
* text width, the text is truncated.
|
||||
* Parameter color: The raw color to draw the text using.
|
||||
* Parameter hilite_mode: Hilighting mode: 0 => off, 1 => Underline, 2 => Invert
|
||||
* Parameter hilite_pos: The character index to hilight. May be length of string (character after end is hilighted).
|
||||
*/
|
||||
void draw_string(SDL_Surface* surf, std::string s, uint32_t x, uint32_t y, uint32_t maxwidth, uint32_t color,
|
||||
uint32_t hilite_mode = 0, uint32_t hilite_pos = 0) throw();
|
||||
|
||||
/**
|
||||
* Draw a box to framebuffer. The contents of box are cleared.
|
||||
*
|
||||
* Parameter base: The base address of framebuffer.
|
||||
* Parameter pitch: Separation between lines in framebuffer in bytes.
|
||||
* Parameter pbytes: Bytes per pixel.
|
||||
* Parameter width: The width of framebuffer.
|
||||
* Parameter height: The height of framebuffer.
|
||||
* Parameter x: The x-coordinate of left edge of inner part of box.
|
||||
* Parameter y: The y-coordinate of top edge of inner part of box.
|
||||
* Parameter w: The width of box inner part. If 0, the box isn't drawn.
|
||||
* Parameter h: The height of box inner part. If 0, the box isn't drawn.
|
||||
* Parameter color: The raw color to draw the box outline using.
|
||||
*/
|
||||
void draw_box(uint8_t* base, uint32_t pitch, uint32_t pbytes, uint32_t width, uint32_t height, uint32_t x, uint32_t y,
|
||||
uint32_t w, uint32_t h, uint32_t color) throw();
|
||||
|
||||
/**
|
||||
* Draw box to SDL surface. The contents of box are cleared.
|
||||
*
|
||||
* Parameter surf: The surface to draw on. Must be locked.
|
||||
* Parameter x: The x-coordinate of left edge of inner part of box.
|
||||
* Parameter y: The y-coordinate of top edge of inner part of box.
|
||||
* Parameter w: The width of box inner part. If 0, the box isn't drawn.
|
||||
* Parameter h: The height of box inner part. If 0, the box isn't drawn.
|
||||
* Parameter color: The raw color to draw the box outline using.
|
||||
*/
|
||||
void draw_box(SDL_Surface* surf, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t color) throw();
|
||||
|
||||
/**
|
||||
* Decode UTF-8 string into codepoints.
|
||||
*/
|
||||
std::vector<uint32_t> decode_utf8(std::string s);
|
||||
|
||||
|
||||
/**
|
||||
* The model for command line.
|
||||
*/
|
||||
struct commandline_model
|
||||
{
|
||||
/**
|
||||
* Create new commandline model.
|
||||
*/
|
||||
commandline_model() throw();
|
||||
/**
|
||||
* Execute key.
|
||||
*
|
||||
* Returns: If key == SPECIAL_ACK, the entered command, otherwise "".
|
||||
*/
|
||||
std::string key(uint32_t key) throw(std::bad_alloc);
|
||||
/**
|
||||
* Do timer tick (for autorepeat).
|
||||
*/
|
||||
void tick() throw(std::bad_alloc);
|
||||
/**
|
||||
* Read the line.
|
||||
*/
|
||||
std::string read_command() throw(std::bad_alloc);
|
||||
/**
|
||||
* Read the cursor position.
|
||||
*/
|
||||
size_t cursor() throw();
|
||||
/**
|
||||
* Is the command line enabled?
|
||||
*/
|
||||
bool enabled() throw();
|
||||
/**
|
||||
* Is in overwrite mode?
|
||||
*/
|
||||
bool overwriting() throw();
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* Parameter surf: The surface to paint on, must be locked.
|
||||
* Parameter x: The x-coordinate to start painting from.
|
||||
* Parameter y: The y-coordinate to start painting from.
|
||||
* Parameter maxwidth: The maximum width.
|
||||
* Parameter color: The raw color.
|
||||
* Parameter box: If true, also paint a box.
|
||||
* Parameter boxcolor: If box is true, this is the raw color to paint the box with.
|
||||
*/
|
||||
void paint(SDL_Surface* surf, uint32_t x, uint32_t y, uint32_t maxwidth, uint32_t color, bool box = false,
|
||||
uint32_t boxcolor = 0xFFFFFFFFUL) throw();
|
||||
private:
|
||||
void scroll_history_up();
|
||||
void scroll_history_down();
|
||||
void handle_cow();
|
||||
void delete_codepoint(size_t idx);
|
||||
bool enabled_flag;
|
||||
uint32_t autorepeating_key;
|
||||
int autorepeat_phase; //0 => Off, 1 => First, 2 => Subsequent.
|
||||
uint32_t autorepeat_counter;
|
||||
std::vector<uint32_t> saved_codepoints;
|
||||
std::vector<uint32_t> codepoints;
|
||||
size_t cursor_pos;
|
||||
bool overwrite_mode;
|
||||
std::list<std::vector<uint32_t>> history;
|
||||
std::list<std::vector<uint32_t>>::iterator history_itr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Status area model.
|
||||
*/
|
||||
struct statusarea_model
|
||||
{
|
||||
/**
|
||||
* Paint the status area.
|
||||
*
|
||||
* Parameter surf: The SDL surface. Must be locked.
|
||||
* Parameter x: Top-left corner x coordinate.
|
||||
* Parameter y: Top-left corner y coordinate.
|
||||
* Parameter w: Width of status area.
|
||||
* Parameter h: height of status area.
|
||||
* Parameter color: Color of text.
|
||||
* Parameter box: If true, draw the bounding box (impiles full redraw).
|
||||
* Parameter boxcolor: The color of surrounding box.
|
||||
*/
|
||||
void paint(SDL_Surface* surf, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t color, bool box = false,
|
||||
uint32_t boxcolor = 0xFFFFFFFFUL) throw();
|
||||
private:
|
||||
std::map<std::string, std::string> current_contents;
|
||||
};
|
||||
|
||||
/**
|
||||
* Messages area model.
|
||||
*/
|
||||
struct messages_model
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
messages_model() throw();
|
||||
/**
|
||||
* Paint the messages area.
|
||||
*
|
||||
* Parameter surf: The SDL surface. Must be locked.
|
||||
* Parameter x: Top-left corner x coordinate.
|
||||
* Parameter y: Top-left corner y coordinate.
|
||||
* Parameter w: Width of status area.
|
||||
* Parameter h: height of status area.
|
||||
* Parameter color: Color of text.
|
||||
* Parameter box: If true, draw the bounding box (impiles full redraw).
|
||||
* Parameter boxcolor: The color of surrounding box.
|
||||
*/
|
||||
void paint(SDL_Surface* surf, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t color, bool box = false,
|
||||
uint32_t boxcolor = 0xFFFFFFFFUL) throw();
|
||||
private:
|
||||
uint64_t first_visible;
|
||||
uint64_t messages_visible;
|
||||
};
|
||||
|
||||
/**
|
||||
* Paint modal message.
|
||||
*
|
||||
* Parameter surf: The SDL surface. Must be locked.
|
||||
* Parameter text: The text for dialog.
|
||||
* Parameter confirm: If true, paint as confirm dialog, otherwise as notification dialog.
|
||||
* Parameter color: Color of the dialog.
|
||||
*/
|
||||
void paint_modal_dialog(SDL_Surface* surf, const std::string& text, bool confirm, uint32_t color) throw();
|
||||
|
||||
/**
|
||||
* Layout of screen.
|
||||
*/
|
||||
struct screen_layout
|
||||
{
|
||||
/**
|
||||
* Default values.
|
||||
*/
|
||||
screen_layout();
|
||||
/**
|
||||
* Update rest of values from real_width/real_height and fullscreen_console.
|
||||
*/
|
||||
void update() throw();
|
||||
/**
|
||||
* Screen real area.
|
||||
*/
|
||||
uint32_t real_width;
|
||||
uint32_t real_height;
|
||||
/**
|
||||
* Fullscreen console mode on.
|
||||
*/
|
||||
bool fullscreen_console;
|
||||
/**
|
||||
* Window.
|
||||
*/
|
||||
uint32_t window_w;
|
||||
uint32_t window_h;
|
||||
/**
|
||||
* The screen.
|
||||
*/
|
||||
uint32_t screen_x;
|
||||
uint32_t screen_y;
|
||||
uint32_t screen_w;
|
||||
uint32_t screen_h;
|
||||
/**
|
||||
* Status area.
|
||||
*/
|
||||
uint32_t status_x;
|
||||
uint32_t status_y;
|
||||
uint32_t status_w;
|
||||
uint32_t status_h;
|
||||
/**
|
||||
* Messages area.
|
||||
*/
|
||||
uint32_t messages_x;
|
||||
uint32_t messages_y;
|
||||
uint32_t messages_w;
|
||||
uint32_t messages_h;
|
||||
/**
|
||||
* Command line.
|
||||
*/
|
||||
uint32_t commandline_x;
|
||||
uint32_t commandline_y;
|
||||
uint32_t commandline_w;
|
||||
};
|
||||
|
||||
/**
|
||||
* Model of screen.
|
||||
*
|
||||
* Don't have multiple of these. The results are undefined. Call all methods from UI thread.
|
||||
*/
|
||||
class screen_model
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
screen_model();
|
||||
/**
|
||||
* Do full repaint.
|
||||
*/
|
||||
void repaint_full() throw();
|
||||
/**
|
||||
* Repaint main screen.
|
||||
*/
|
||||
void repaint_screen() throw();
|
||||
/**
|
||||
* Repaint status area.
|
||||
*/
|
||||
void repaint_status() throw();
|
||||
/**
|
||||
* Repaint message area.
|
||||
*/
|
||||
void repaint_messages() throw();
|
||||
/**
|
||||
* Repaint command line.
|
||||
*/
|
||||
void repaint_commandline() throw();
|
||||
/**
|
||||
* Set the command line model to use.
|
||||
*/
|
||||
void set_command_line(commandline_model* c) throw();
|
||||
/**
|
||||
* Set modal dialog.
|
||||
*/
|
||||
void set_modal(const std::string& text, bool confirm) throw();
|
||||
/**
|
||||
* Clear modal dialog.
|
||||
*/
|
||||
void clear_modal() throw();
|
||||
/**
|
||||
* Set fullscreen console mode on/off
|
||||
*/
|
||||
void set_fullscreen_console(bool enable) throw();
|
||||
/**
|
||||
* Do SDL_Flip or equivalent.
|
||||
*/
|
||||
void flip() throw();
|
||||
/**
|
||||
* Temporary palette values.
|
||||
*/
|
||||
unsigned pal_r;
|
||||
unsigned pal_g;
|
||||
unsigned pal_b;
|
||||
private:
|
||||
void _repaint_screen() throw();
|
||||
void repaint_modal() throw();
|
||||
SDL_Surface* surf;
|
||||
commandline_model* cmdline;
|
||||
bool modal_active;
|
||||
std::string modal_text;
|
||||
bool modal_confirm;
|
||||
statusarea_model statusarea;
|
||||
messages_model smessages;
|
||||
screen_layout layout;
|
||||
uint32_t old_screen_w;
|
||||
uint32_t old_screen_h;
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify the UI code that emulator thread is about to exit (call in the emulator thread).
|
||||
*/
|
||||
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
|
312
manual.lyx
312
manual.lyx
|
@ -721,117 +721,6 @@ run-script <script>
|
|||
Run <script> as if commands were entered on the command line.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsection
|
||||
Video dumping
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Following commands control video dumping:
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
start-dump <dumper> [<mode>] <prefix/filename>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Start dumping using dumper <dumper>.
|
||||
If mode is present or not and if prefix or filename is present depends
|
||||
on the dumper and dumper mode.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
The following dumpers are available:
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
INTERNAL-AVI-CSCD: Internal CSCD in .avi dumper.
|
||||
\end_layout
|
||||
|
||||
\begin_deeper
|
||||
\begin_layout Itemize
|
||||
Mode: uncompressed/pcm: Uncompressed video, PCM audio.
|
||||
Takes prefix.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
Mode: cscd/pcm: CSCD video, PCM audio.
|
||||
Takes prefix.
|
||||
\end_layout
|
||||
|
||||
\end_deeper
|
||||
\begin_layout Itemize
|
||||
INTERNAL-JMD: Internal .jmd dumper.
|
||||
\end_layout
|
||||
|
||||
\begin_deeper
|
||||
\begin_layout Itemize
|
||||
Does not take mode.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
Takes a filename.
|
||||
\end_layout
|
||||
|
||||
\end_deeper
|
||||
\begin_layout Itemize
|
||||
INTERNAL-RAW: Internal RAW dumper.
|
||||
\end_layout
|
||||
|
||||
\begin_deeper
|
||||
\begin_layout Itemize
|
||||
Does not take mode.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
Takes a prefix.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
Sound is big-endian signed 16-bit, usually at 32040.5Hz.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
Video is always upscaled to double resolution (512x448 / 512 x 478).
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
Video framerate is usually 322445/6448 fps for PAL and 10738636/178683 fps
|
||||
for NTSC.
|
||||
\end_layout
|
||||
|
||||
\end_deeper
|
||||
\begin_layout Itemize
|
||||
INTERNAL-SDMP: Internal SDMP dumper.
|
||||
\end_layout
|
||||
|
||||
\begin_deeper
|
||||
\begin_layout Itemize
|
||||
Mode 'ms': Multi-segment.
|
||||
Takes prefix.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
Mode 'ss': Single-segment.
|
||||
Takes filename.
|
||||
\end_layout
|
||||
|
||||
\end_deeper
|
||||
\begin_layout Subsubsection
|
||||
end-dump <dumper>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
End dumping using <dumper>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
show-dumpers [<dumper>]
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Show the list of dumpers or list of modes for <dumper>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsection
|
||||
Memory manipulation
|
||||
\end_layout
|
||||
|
@ -1418,30 +1307,6 @@ enable-sound <on/off>
|
|||
Enable/Disable sound.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
set-sound-device <device>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Set sound device to <device>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
show-sound-status
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Show status of sound system.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
show-sound-devices
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Show all available devices.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
set-volume <multiplier>
|
||||
\end_layout
|
||||
|
@ -1482,119 +1347,6 @@ reload-rom [<file>]
|
|||
Reloads the main ROM image from <file>.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
edit-subtitle <firstframe> <length> [<text>]
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Edit or delete a subtitle.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
list-subtitle
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
List subtitles.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsection
|
||||
Commentary track
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
list-streams
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
List streams
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
play-stream <id>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Play stream <id>.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
delete-stream <id>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Delete stream <id>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
change-timebase <id> <newbase>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Change time base of <id> to <newbase>.
|
||||
The time base can be given as samples (integer) or second (postfix with
|
||||
s).
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
import-stream-opus <timebase> <file>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Import opus stream from <file>, placing it at <timebase>.
|
||||
This operation is lossless.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
import-stream-pcm <timebase> <file>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Import PCM (.sox, 1ch@48kHz) stream from <file>, placing it at <timebase>.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
export-stream-opus <id> <file>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Export stream <id> as opus stream to <file>.
|
||||
This operation is lossless.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
export-stream-pcm <id> <file>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Export stream <id> as PCM (.sox) stream to <file>.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
export-superstream <file>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Export the entiere superstream as PCM (.sox) stream to <file>.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
load-collection <file>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Load collection (if there is existing collection, unload it) from <file>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
unload-collection
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Unload collection.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
+tangent
|
||||
\end_layout
|
||||
|
@ -1604,70 +1356,6 @@ Tangent for recording voice for commentary track.
|
|||
While pressed, record a stream.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsection
|
||||
SDL Platform commands
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
The following are valid on SDL platform.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
identify-key
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Asks to press a key and then identifies that (pseudo-)key.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
toggle-console
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Toggle between windowed/fullscreen console.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
scroll-fullup
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Scroll messages window as far back as it goes.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
scroll-fulldown
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Scroll messages window as far forward as it goes.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
scroll-up
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Scroll messages window back one screenful.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
scroll-down
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Scroll messages window forward one screenful.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
prompt-command <command>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Enter command prompt, with prompt prepopulated with specified command.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Section
|
||||
Settings
|
||||
\end_layout
|
||||
|
|
275
manual.txt
275
manual.txt
|
@ -333,60 +333,7 @@ Print all aliases and their expansions in effect.
|
|||
|
||||
Run <script> as if commands were entered on the command line.
|
||||
|
||||
6.5 Video dumping
|
||||
|
||||
Following commands control video dumping:
|
||||
|
||||
6.5.1 start-dump <dumper> [<mode>] <prefix/filename>
|
||||
|
||||
Start dumping using dumper <dumper>. If mode is present or not
|
||||
and if prefix or filename is present depends on the dumper and
|
||||
dumper mode.
|
||||
|
||||
The following dumpers are available:
|
||||
|
||||
• INTERNAL-AVI-CSCD: Internal CSCD in .avi dumper.
|
||||
|
||||
– Mode: uncompressed/pcm: Uncompressed video, PCM audio. Takes
|
||||
prefix.
|
||||
|
||||
– Mode: cscd/pcm: CSCD video, PCM audio. Takes prefix.
|
||||
|
||||
• INTERNAL-JMD: Internal .jmd dumper.
|
||||
|
||||
– Does not take mode.
|
||||
|
||||
– Takes a filename.
|
||||
|
||||
• INTERNAL-RAW: Internal RAW dumper.
|
||||
|
||||
– Does not take mode.
|
||||
|
||||
– Takes a prefix.
|
||||
|
||||
– Sound is big-endian signed 16-bit, usually at 32040.5Hz.
|
||||
|
||||
– Video is always upscaled to double resolution (512x448 / 512
|
||||
x 478).
|
||||
|
||||
– Video framerate is usually 322445/6448 fps for PAL and
|
||||
10738636/178683 fps for NTSC.
|
||||
|
||||
• INTERNAL-SDMP: Internal SDMP dumper.
|
||||
|
||||
– Mode 'ms': Multi-segment. Takes prefix.
|
||||
|
||||
– Mode 'ss': Single-segment. Takes filename.
|
||||
|
||||
6.5.2 end-dump <dumper>
|
||||
|
||||
End dumping using <dumper>
|
||||
|
||||
6.5.3 show-dumpers [<dumper>]
|
||||
|
||||
Show the list of dumpers or list of modes for <dumper>
|
||||
|
||||
6.6 Memory manipulation
|
||||
6.5 Memory manipulation
|
||||
|
||||
<address> may be decimal or hexadecimal (prefixed with '0x').
|
||||
<value> can be hexadecimal (prefixed with '0x'), unsigned or
|
||||
|
@ -407,31 +354,31 @@ When dealing with DSP memory, multi-byte reads/writes are
|
|||
native-endian (do not use operand sizes exceeding DSP bitness,
|
||||
except dword is OK for 24-bit memory).
|
||||
|
||||
6.6.1 read-<size> <address>
|
||||
6.5.1 read-<size> <address>
|
||||
|
||||
Read the value of byte in <address>.
|
||||
|
||||
6.6.2 read-s<size> <address>
|
||||
6.5.2 read-s<size> <address>
|
||||
|
||||
Read the value of signed byte in <address>.
|
||||
|
||||
6.6.3 write-<size> <address> <value>
|
||||
6.5.3 write-<size> <address> <value>
|
||||
|
||||
Write <value> to byte in address <address>.
|
||||
|
||||
6.6.4 search-memory reset
|
||||
6.5.4 search-memory reset
|
||||
|
||||
Reset the memory search
|
||||
|
||||
6.6.5 search-memory count
|
||||
6.5.5 search-memory count
|
||||
|
||||
Print number of candidates remaining
|
||||
|
||||
6.6.6 search-memory print
|
||||
6.5.6 search-memory print
|
||||
|
||||
Print all candidates remaining
|
||||
|
||||
6.6.7 search-memory <usflag><sizeflag><op>
|
||||
6.5.7 search-memory <usflag><sizeflag><op>
|
||||
|
||||
Searches memory for addresses satisfying criteria.
|
||||
|
||||
|
@ -465,122 +412,122 @@ Searches memory for addresses satisfying criteria.
|
|||
|
||||
• gt: > previous value.
|
||||
|
||||
6.6.8 search-memory <sizeflag> <value>
|
||||
6.5.8 search-memory <sizeflag> <value>
|
||||
|
||||
Searches for addresses that currently have value <value>.
|
||||
<sizeflag> is as in previous command.
|
||||
|
||||
6.7 Main commands
|
||||
6.6 Main commands
|
||||
|
||||
These commands are not available in lsnesrc, but are available
|
||||
after ROM has been loaded.
|
||||
|
||||
6.7.1 quit-emulator [/y]
|
||||
6.6.1 quit-emulator [/y]
|
||||
|
||||
Quits the emulator (asking for confirmation). If /y is given, no
|
||||
confirmation is asked.
|
||||
|
||||
6.7.2 pause-emulator
|
||||
6.6.2 pause-emulator
|
||||
|
||||
Toggle paused/unpaused
|
||||
|
||||
6.7.3 +advance-frame
|
||||
6.6.3 +advance-frame
|
||||
|
||||
Advance frame. If the button is still held after configurable
|
||||
timeout expires, game unpauses for the duration frame advance is
|
||||
held.
|
||||
|
||||
6.7.4 +advance-poll
|
||||
6.6.4 +advance-poll
|
||||
|
||||
Advance subframe. If the button is still held after configurable
|
||||
timeout expires, game unpauses for the duration frame advance is
|
||||
held.
|
||||
|
||||
6.7.5 advance-skiplag
|
||||
6.6.5 advance-skiplag
|
||||
|
||||
Skip to first poll in frame after current.
|
||||
|
||||
6.7.6 reset
|
||||
6.6.6 reset
|
||||
|
||||
Reset the SNES after this frame.
|
||||
|
||||
6.7.7 load <filename>
|
||||
6.6.7 load <filename>
|
||||
|
||||
Load savestate <filename> in current mode.
|
||||
|
||||
6.7.8 load-state <filename>
|
||||
6.6.8 load-state <filename>
|
||||
|
||||
Load savestate <filename> in readwrite mode.
|
||||
|
||||
6.7.9 load-readonly <filename>
|
||||
6.6.9 load-readonly <filename>
|
||||
|
||||
Load savestate <filename> in readonly mode.
|
||||
|
||||
6.7.10 load-preserve <filename>
|
||||
6.6.10 load-preserve <filename>
|
||||
|
||||
Load savestate <filename> in readonly mode, preserving current
|
||||
events.
|
||||
|
||||
6.7.11 load-movie <filename>
|
||||
6.6.11 load-movie <filename>
|
||||
|
||||
Load savestate <filename>, ignoring save part in readonly mode.
|
||||
|
||||
6.7.12 save-state <filename>
|
||||
6.6.12 save-state <filename>
|
||||
|
||||
Save system state to <filename> as soon as possible.
|
||||
|
||||
6.7.13 save-movie <filename>
|
||||
6.6.13 save-movie <filename>
|
||||
|
||||
Save movie to <filename>.
|
||||
|
||||
6.7.14 set-rwmode
|
||||
6.6.14 set-rwmode
|
||||
|
||||
Set read-write mode.
|
||||
|
||||
6.7.15 set-romode
|
||||
6.6.15 set-romode
|
||||
|
||||
Set read-only mode
|
||||
|
||||
6.7.16 toggle-rwmode
|
||||
6.6.16 toggle-rwmode
|
||||
|
||||
Toggle between read-only and read-write modes.
|
||||
|
||||
6.7.17 set-gamename <name>
|
||||
6.6.17 set-gamename <name>
|
||||
|
||||
Set name of the game to <name>
|
||||
|
||||
6.7.18 get-gamename
|
||||
6.6.18 get-gamename
|
||||
|
||||
Print the name of the game.
|
||||
|
||||
6.7.19 add-author <author>
|
||||
6.6.19 add-author <author>
|
||||
|
||||
Adds new author <author>. If <author> does not contain '|' it is
|
||||
full name. If it contains '|', '|' splits the full name and
|
||||
nickname.
|
||||
|
||||
6.7.20 edit-author <num> <author>
|
||||
6.6.20 edit-author <num> <author>
|
||||
|
||||
Edit the author in slot <num> (0-based) to be <author> (see
|
||||
add-author for format)
|
||||
|
||||
6.7.21 remove-author <num>
|
||||
6.6.21 remove-author <num>
|
||||
|
||||
Remove author in slot <num>
|
||||
|
||||
6.7.22 print-authors
|
||||
6.6.22 print-authors
|
||||
|
||||
Print authors.
|
||||
|
||||
6.7.23 test-1, test-2, test-3
|
||||
6.6.23 test-1, test-2, test-3
|
||||
|
||||
Internal test commands. Don't use.
|
||||
|
||||
6.7.24 take-screenshot <filename>
|
||||
6.6.24 take-screenshot <filename>
|
||||
|
||||
Save screenshot to <filename>.
|
||||
|
||||
6.7.25 +controller<num><button>
|
||||
6.6.25 +controller<num><button>
|
||||
|
||||
Press button <button> on controller <num> (1-8). The following
|
||||
button names are known:
|
||||
|
@ -617,209 +564,105 @@ button names are known:
|
|||
|
||||
• turbo
|
||||
|
||||
6.7.26 controllerh<num><button>
|
||||
6.6.26 controllerh<num><button>
|
||||
|
||||
Hold/unhold button <button> on controller <num> (1-8). See
|
||||
+controller for button names.
|
||||
|
||||
6.7.27 controllerf<num><button>
|
||||
6.6.27 controllerf<num><button>
|
||||
|
||||
Hold/unhold button <button> on controller <num> (1-8) for the
|
||||
next frame. See +controller for button names.
|
||||
|
||||
Cauntion: Does not work properly if outside frame advance.
|
||||
|
||||
6.7.28 autofire (<pattern>|-)...
|
||||
6.6.28 autofire (<pattern>|-)...
|
||||
|
||||
Set autofire pattern. Each parameter is comma-separated list of
|
||||
button names (in form of 1start, 1A, 2B, etc..) to hold on that
|
||||
frame. After reaching the end of pattern, the pattern restarts
|
||||
from the beginning.
|
||||
|
||||
6.7.29 repaint
|
||||
6.6.29 repaint
|
||||
|
||||
Force a repaint.
|
||||
|
||||
6.8 Save jukebox
|
||||
6.7 Save jukebox
|
||||
|
||||
6.8.1 cycle-jukebox-backward
|
||||
6.7.1 cycle-jukebox-backward
|
||||
|
||||
Cycle save jukebox backwards.
|
||||
|
||||
6.8.2 cycle-jukebox-forward
|
||||
6.7.2 cycle-jukebox-forward
|
||||
|
||||
Cycle save jukebox forwards
|
||||
|
||||
6.8.3 load-jukebox
|
||||
6.7.3 load-jukebox
|
||||
|
||||
Do load from jukebox (current mode).
|
||||
|
||||
6.8.4 save-jukebox
|
||||
6.7.4 save-jukebox
|
||||
|
||||
Do state save to jukebox.
|
||||
|
||||
6.9 Lua
|
||||
6.8 Lua
|
||||
|
||||
Only available if lua support is compiled in.
|
||||
|
||||
6.9.1 evaluate-lua <luacode>
|
||||
6.8.1 evaluate-lua <luacode>
|
||||
|
||||
Run Lua code <luacode> using built-in Lua interpretter.
|
||||
|
||||
6.9.2 run-lua <script>
|
||||
6.8.2 run-lua <script>
|
||||
|
||||
Run specified lua file using built-in Lua interpretter.
|
||||
|
||||
6.9.3 reset-lua
|
||||
6.8.3 reset-lua
|
||||
|
||||
Clear the Lua VM state and restore to factory defaults.
|
||||
|
||||
6.10 Memory watch
|
||||
6.9 Memory watch
|
||||
|
||||
6.10.1 add-watch <name> <expression>
|
||||
6.9.1 add-watch <name> <expression>
|
||||
|
||||
Adds new watch (or modifies old one).
|
||||
|
||||
6.10.2 remove-watch <name>
|
||||
6.9.2 remove-watch <name>
|
||||
|
||||
Remove a watch.
|
||||
|
||||
6.11 Sound
|
||||
6.10 Sound
|
||||
|
||||
6.11.1 enable-sound <on/off>
|
||||
6.10.1 enable-sound <on/off>
|
||||
|
||||
Enable/Disable sound.
|
||||
|
||||
6.11.2 set-sound-device <device>
|
||||
|
||||
Set sound device to <device>
|
||||
|
||||
6.11.3 show-sound-status
|
||||
|
||||
Show status of sound system.
|
||||
|
||||
6.11.4 show-sound-devices
|
||||
|
||||
Show all available devices.
|
||||
|
||||
6.11.5 set-volume <multiplier>
|
||||
6.10.2 set-volume <multiplier>
|
||||
|
||||
Set the volume multiplier to <multiplier>. 1 is normal volume,
|
||||
and higher numbers are louder.
|
||||
|
||||
6.11.6 set-volume <multiplier>%
|
||||
6.10.3 set-volume <multiplier>%
|
||||
|
||||
Set the volume multiplier to <multiplier> percent. 100 is normal
|
||||
volume, and higher numbers are louder.
|
||||
|
||||
6.11.7 set-volume <multiplier>dB
|
||||
6.10.4 set-volume <multiplier>dB
|
||||
|
||||
Set the volume multiplier to <multiplier> dB. 0 is normal volume,
|
||||
and higher numbers are louder. The value may be negative.
|
||||
|
||||
6.12 Misc.
|
||||
6.11 Misc.
|
||||
|
||||
6.12.1 reload-rom [<file>]
|
||||
6.11.1 reload-rom [<file>]
|
||||
|
||||
Reloads the main ROM image from <file>.
|
||||
|
||||
6.12.2 edit-subtitle <firstframe> <length> [<text>]
|
||||
|
||||
Edit or delete a subtitle.
|
||||
|
||||
6.12.3 list-subtitle
|
||||
|
||||
List subtitles.
|
||||
|
||||
6.13 Commentary track
|
||||
|
||||
6.13.1 list-streams
|
||||
|
||||
List streams
|
||||
|
||||
6.13.2 play-stream <id>
|
||||
|
||||
Play stream <id>.
|
||||
|
||||
6.13.3 delete-stream <id>
|
||||
|
||||
Delete stream <id>
|
||||
|
||||
6.13.4 change-timebase <id> <newbase>
|
||||
|
||||
Change time base of <id> to <newbase>. The time base can be given
|
||||
as samples (integer) or second (postfix with s).
|
||||
|
||||
6.13.5 import-stream-opus <timebase> <file>
|
||||
|
||||
Import opus stream from <file>, placing it at <timebase>. This
|
||||
operation is lossless.
|
||||
|
||||
6.13.6 import-stream-pcm <timebase> <file>
|
||||
|
||||
Import PCM (.sox, 1ch@48kHz) stream from <file>, placing it at
|
||||
<timebase>.
|
||||
|
||||
6.13.7 export-stream-opus <id> <file>
|
||||
|
||||
Export stream <id> as opus stream to <file>. This operation is
|
||||
lossless.
|
||||
|
||||
6.13.8 export-stream-pcm <id> <file>
|
||||
|
||||
Export stream <id> as PCM (.sox) stream to <file>.
|
||||
|
||||
6.13.9 export-superstream <file>
|
||||
|
||||
Export the entiere superstream as PCM (.sox) stream to <file>.
|
||||
|
||||
6.13.10 load-collection <file>
|
||||
|
||||
Load collection (if there is existing collection, unload it) from
|
||||
<file>
|
||||
|
||||
6.13.11 unload-collection
|
||||
|
||||
Unload collection.
|
||||
|
||||
6.13.12 +tangent
|
||||
6.11.2 +tangent
|
||||
|
||||
Tangent for recording voice for commentary track. While pressed,
|
||||
record a stream.
|
||||
|
||||
6.14 SDL Platform commands
|
||||
|
||||
The following are valid on SDL platform.
|
||||
|
||||
6.14.1 identify-key
|
||||
|
||||
Asks to press a key and then identifies that (pseudo-)key.
|
||||
|
||||
6.14.2 toggle-console
|
||||
|
||||
Toggle between windowed/fullscreen console.
|
||||
|
||||
6.14.3 scroll-fullup
|
||||
|
||||
Scroll messages window as far back as it goes.
|
||||
|
||||
6.14.4 scroll-fulldown
|
||||
|
||||
Scroll messages window as far forward as it goes.
|
||||
|
||||
6.14.5 scroll-up
|
||||
|
||||
Scroll messages window back one screenful.
|
||||
|
||||
6.14.6 scroll-down
|
||||
|
||||
Scroll messages window forward one screenful.
|
||||
|
||||
6.14.7 prompt-command <command>
|
||||
|
||||
Enter command prompt, with prompt prepopulated with specified
|
||||
command.
|
||||
|
||||
7 Settings
|
||||
|
||||
7.1 Core settings
|
||||
|
|
|
@ -11,84 +11,6 @@
|
|||
namespace
|
||||
{
|
||||
globalwrap<std::map<std::string, adv_dumper*>> dumpers;
|
||||
|
||||
adv_dumper& find_by_name(const std::string& dname)
|
||||
{
|
||||
auto i = adv_dumper::get_dumper_set();
|
||||
for(auto j : i)
|
||||
if(j->id() == dname)
|
||||
return *j;
|
||||
throw std::runtime_error("Unknown dumper");
|
||||
}
|
||||
|
||||
function_ptr_command<const std::string&> start_dump(lsnes_cmd, "start-dump", "Start dumping",
|
||||
"Syntax: start-dump <dumper> <prefix/filename>\nSyntax: start-dump <dumper> <mode> <prefix/filename>\n"
|
||||
"Start dumping using <dumper> in mode <mode> to <prefix/filename>\n",
|
||||
[](const std::string& t) throw(std::bad_alloc, std::runtime_error) {
|
||||
std::string t2 = t;
|
||||
std::string dumper;
|
||||
extract_token(t2, dumper, " \t", true);
|
||||
adv_dumper& d = find_by_name(dumper);
|
||||
auto modes = d.list_submodes();
|
||||
std::string mode;
|
||||
if(!modes.empty()) {
|
||||
extract_token(t2, mode, " \t", true);
|
||||
if(!modes.count(mode))
|
||||
throw std::runtime_error("Bad mode for dumper");
|
||||
}
|
||||
if(t2 == "")
|
||||
throw std::runtime_error("Command syntax error");
|
||||
d.start(mode, t2);
|
||||
});
|
||||
|
||||
function_ptr_command<const std::string&> end_dump(lsnes_cmd, "end-dump", "End dumping",
|
||||
"Syntax: end-dump <dumper>\nEnd dumping using dumper <dumper>\n",
|
||||
[](const std::string& t) throw(std::bad_alloc, std::runtime_error) {
|
||||
auto r = regex("([^ \t]+)[ \t]*", t, "Command syntax error");
|
||||
adv_dumper& d = find_by_name(r[1]);
|
||||
d.end();
|
||||
});
|
||||
|
||||
function_ptr_command<const std::string&> dumpersc(lsnes_cmd, "show-dumpers", "Show dumpers",
|
||||
"Syntax: show-dumpers\nSyntax: show-dumpers <dumper>\nShow dumpers or dumper modes for <dumper>\n",
|
||||
[](const std::string& x) throw(std::bad_alloc, std::runtime_error) {
|
||||
auto a = adv_dumper::get_dumper_set();
|
||||
if(x == "") {
|
||||
for(auto i : a)
|
||||
messages << i->id() << "\t" << i->name() << std::endl;
|
||||
} else {
|
||||
for(auto i : a) {
|
||||
if(i->id() == x) {
|
||||
//This dumper.
|
||||
auto b = i->list_submodes();
|
||||
if(b.empty()) {
|
||||
messages << "No submodes for '" << x << "'" << std::endl;
|
||||
return;
|
||||
}
|
||||
for(auto j : b) {
|
||||
unsigned d = i->mode_details(j);
|
||||
if((d & adv_dumper::target_type_mask) ==
|
||||
adv_dumper::target_type_prefix)
|
||||
messages << "P " << x << "\t" << j << "\t"
|
||||
<< i->modename(j) << std::endl;
|
||||
else if((d & adv_dumper::target_type_mask) ==
|
||||
adv_dumper::target_type_file)
|
||||
messages << "F " << x << "\t" << j << "\t"
|
||||
<< i->modename(j) << std::endl;
|
||||
else if((d & adv_dumper::target_type_mask) ==
|
||||
adv_dumper::target_type_special)
|
||||
messages << "S " << x << "\t" << j << "\t"
|
||||
<< i->modename(j) << std::endl;
|
||||
else
|
||||
messages << "U " << x << "\t" << j << "\t"
|
||||
<< i->modename(j) << std::endl;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
messages << "No such dumper '" << x << "'" << std::endl;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const std::string& adv_dumper::id() throw()
|
||||
|
|
|
@ -1802,8 +1802,8 @@ out:
|
|||
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||
active_flag = false;
|
||||
});
|
||||
inverse_key itangent("+tangent", "Movie‣Voice tangent");
|
||||
inthread_th* int_task;
|
||||
|
||||
}
|
||||
|
||||
void voice_frame_number(uint64_t newframe, double rate)
|
||||
|
@ -1839,250 +1839,6 @@ uint64_t voicesub_parse_timebase(const std::string& n)
|
|||
return parse_value<uint64_t>(x);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
function_ptr_command<> list_streams(lsnes_cmd, "list-streams", "List streams ", "list-streams\n"
|
||||
"List known voice streams",
|
||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||
umutex_class m2(current_collection_lock);
|
||||
if(!current_collection) {
|
||||
messages << "No voice streams loaded." << std::endl;
|
||||
return;
|
||||
}
|
||||
messages << "-----------------------" << std::endl;
|
||||
for(auto i : current_collection->all_streams()) {
|
||||
opus_stream* s = current_collection->get_stream(i);
|
||||
if(!s)
|
||||
continue;
|
||||
messages << "ID #" << i << ": base=" << s->timebase() << " ("
|
||||
<< (s->timebase() / 48000.0) << "s), length=" << s->length() << " ("
|
||||
<< (s->length() / 48000.0) << "s)" << std::endl;
|
||||
s->put_ref();
|
||||
}
|
||||
messages << "-----------------------" << std::endl;
|
||||
});
|
||||
|
||||
function_ptr_command<const std::string&> delete_stream(lsnes_cmd, "delete-stream", "Delete a stream",
|
||||
"delete-stream <id>\nDelete a voice stream with given ID.",
|
||||
[](const std::string& x) throw(std::bad_alloc, std::runtime_error) {
|
||||
umutex_class m2(current_collection_lock);
|
||||
uint64_t id = parse_value<uint64_t>(x);
|
||||
if(!current_collection) {
|
||||
messages << "No voice streams loaded." << std::endl;
|
||||
return;
|
||||
}
|
||||
opus_stream* s = current_collection->get_stream(id);
|
||||
if(!s) {
|
||||
messages << "Error, no such stream found." << std::endl;
|
||||
return;
|
||||
}
|
||||
s->put_ref();
|
||||
current_collection->delete_stream(id);
|
||||
messages << "Deleted stream #" << id << "." << std::endl;
|
||||
});
|
||||
|
||||
function_ptr_command<const std::string&> play_stream(lsnes_cmd, "play-stream", "Play a stream",
|
||||
"play-stream <id>\nPlay a voice stream with given ID.",
|
||||
[](const std::string& x) throw(std::bad_alloc, std::runtime_error) {
|
||||
umutex_class m2(current_collection_lock);
|
||||
uint64_t id = parse_value<uint64_t>(x);
|
||||
if(!current_collection) {
|
||||
messages << "No voice streams loaded." << std::endl;
|
||||
return;
|
||||
}
|
||||
opus_stream* s = current_collection->get_stream(id);
|
||||
if(!s) {
|
||||
messages << "Error, no such stream found." << std::endl;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
start_management_stream(*s);
|
||||
} catch(...) {
|
||||
s->put_ref();
|
||||
throw;
|
||||
}
|
||||
s->put_ref();
|
||||
messages << "Playing stream #" << id << "." << std::endl;
|
||||
});
|
||||
|
||||
function_ptr_command<const std::string&> change_timebase(lsnes_cmd, "change-timebase",
|
||||
"Change stream timebase", "change-timebase <id> <newbase>\nChange timebase of given stream",
|
||||
[](const std::string& x) throw(std::bad_alloc, std::runtime_error) {
|
||||
umutex_class m2(current_collection_lock);
|
||||
if(!current_collection) {
|
||||
messages << "No voice streams loaded." << std::endl;
|
||||
return;
|
||||
}
|
||||
auto r = regex("([0-9]+)[ \t]+([^ \t]*)", x);
|
||||
if(!r) {
|
||||
messages << "Syntax: change-timebase <id> <timebase>" << std::endl;
|
||||
return;
|
||||
}
|
||||
uint64_t id = parse_value<uint64_t>(r[1]);
|
||||
uint64_t tbase = voicesub_parse_timebase(r[2]);
|
||||
opus_stream* s = current_collection->get_stream(id);
|
||||
if(!s) {
|
||||
messages << "Error, no such stream found." << std::endl;
|
||||
return;
|
||||
}
|
||||
s->put_ref();
|
||||
current_collection->alter_stream_timebase(id, tbase);
|
||||
messages << "Timebase of stream #" << id << " is now " << (tbase / 48000.0) << "s"
|
||||
<< std::endl;
|
||||
});
|
||||
|
||||
void import_cmd_common(const std::string& x, const char* postfix, external_stream_format mode)
|
||||
{
|
||||
umutex_class m2(current_collection_lock);
|
||||
if(!current_collection) {
|
||||
messages << "No voice streams loaded." << std::endl;
|
||||
return;
|
||||
}
|
||||
auto r = regex("([^ \t]+)[ \t]+(.+)", x);
|
||||
if(!r) {
|
||||
messages << "Syntax: import-stream-" << postfix << " <timebase> <filename>" << std::endl;
|
||||
return;
|
||||
}
|
||||
uint64_t tbase = voicesub_parse_timebase(r[1]);
|
||||
std::string fname = r[2];
|
||||
std::ifstream s(fname, std::ios_base::in | std::ios_base::binary);
|
||||
if(!s) {
|
||||
messages << "Can't open '" << fname << "'" << std::endl;
|
||||
return;
|
||||
}
|
||||
opus_stream* st = new opus_stream(tbase, current_collection->get_filesystem(), s, mode);
|
||||
uint64_t id;
|
||||
try {
|
||||
id = current_collection->add_stream(*st);
|
||||
} catch(...) {
|
||||
st->delete_stream();
|
||||
throw;
|
||||
}
|
||||
st->unlock(); //Not locked.
|
||||
messages << "Imported stream (" << st->length() / 48000.0 << "s) as ID #" << id << std::endl;
|
||||
}
|
||||
|
||||
function_ptr_command<const std::string&> import_stream_c(lsnes_cmd, "import-stream-opus", "Import a opus "
|
||||
"stream", "import-stream-opus <timebase> <filename>\nImport opus stream from <filename>, starting at "
|
||||
"<timebase>",
|
||||
[](const std::string& x) throw(std::bad_alloc, std::runtime_error) {
|
||||
import_cmd_common(x, "opus", EXTFMT_OPUSDEMO);
|
||||
});
|
||||
|
||||
function_ptr_command<const std::string&> import_stream_p(lsnes_cmd, "import-stream-pcm", "Import a PCM "
|
||||
"stream", "import-stream-pcm <timebase> <filename>\nImport PCM stream from <filename>, starting at "
|
||||
"<timebase>",
|
||||
[](const std::string& x) throw(std::bad_alloc, std::runtime_error) {
|
||||
import_cmd_common(x, "pcm", EXTFMT_SOX);
|
||||
});
|
||||
|
||||
function_ptr_command<const std::string&> import_stream_o(lsnes_cmd, "import-stream-ogg",
|
||||
"Import a OggOpus stream", "import-stream-ogg <timebase> <filename>\nImport OggOpus stream from "
|
||||
" <filename>, starting at <timebase>",
|
||||
[](const std::string& x) throw(std::bad_alloc, std::runtime_error) {
|
||||
import_cmd_common(x, "ogg", EXTFMT_OGGOPUS);
|
||||
});
|
||||
|
||||
void export_cmd_common(const std::string& x, const char* postfix, external_stream_format mode)
|
||||
{
|
||||
umutex_class m2(current_collection_lock);
|
||||
if(!current_collection) {
|
||||
messages << "No voice streams loaded." << std::endl;
|
||||
return;
|
||||
}
|
||||
auto r = regex("([0-9]+)[ \t]+(.+)", x);
|
||||
if(!r) {
|
||||
messages << "Syntax: export-stream-" << postfix << " <id> <filename>" << std::endl;
|
||||
return;
|
||||
}
|
||||
uint64_t id = parse_value<uint64_t>(r[1]);
|
||||
std::string fname = r[2];
|
||||
std::ofstream s(fname, std::ios_base::out | std::ios_base::binary);
|
||||
if(!s) {
|
||||
messages << "Can't open '" << fname << "'" << std::endl;
|
||||
return;
|
||||
}
|
||||
opus_stream* st = current_collection->get_stream(id);
|
||||
if(!st) {
|
||||
messages << "Error, stream #" << id << " does not exist." << std::endl;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
st->export_stream(s, mode);
|
||||
messages << "Exported stream #" << id << " (" << st->length() / 48000.0 << "s)" << std::endl;
|
||||
} catch(std::exception& e) {
|
||||
messages << "Export failed: " << e.what();
|
||||
}
|
||||
st->put_ref();
|
||||
}
|
||||
|
||||
function_ptr_command<const std::string&> export_stream_c(lsnes_cmd, "export-stream-opus", "Export a opus "
|
||||
"stream", "export-stream-opus <id> <filename>\nExport opus stream <id> to <filename>",
|
||||
[](const std::string& x) throw(std::bad_alloc, std::runtime_error) {
|
||||
export_cmd_common(x, "opus", EXTFMT_OPUSDEMO);
|
||||
});
|
||||
|
||||
function_ptr_command<const std::string&> export_stream_p(lsnes_cmd, "export-stream-pcm",
|
||||
"Export a PCM stream", "export-stream-pcm <id> <filename>\nExport PCM stream <id> to <filename>",
|
||||
[](const std::string& x) throw(std::bad_alloc, std::runtime_error) {
|
||||
export_cmd_common(x, "pcm", EXTFMT_SOX);
|
||||
});
|
||||
|
||||
function_ptr_command<const std::string&> export_stream_o(lsnes_cmd, "export-stream-ogg",
|
||||
"Export a OggOpus stream", "export-stream-ogg <id> <filename>\nExport OggOpus stream <id> to "
|
||||
"<filename>",
|
||||
[](const std::string& x) throw(std::bad_alloc, std::runtime_error) {
|
||||
export_cmd_common(x, "ogg", EXTFMT_OGGOPUS);
|
||||
});
|
||||
|
||||
function_ptr_command<const std::string&> export_sstream(lsnes_cmd, "export-superstream", "Export superstream",
|
||||
"export-superstream <filename>\nExport PCM superstream to <filename>",
|
||||
[](const std::string& x) throw(std::bad_alloc, std::runtime_error) {
|
||||
umutex_class m2(current_collection_lock);
|
||||
if(!current_collection)
|
||||
return;
|
||||
std::ofstream s(x, std::ios_base::out | std::ios_base::binary);
|
||||
if(!s) {
|
||||
messages << "Can't open '" << x << "'" << std::endl;
|
||||
return;
|
||||
}
|
||||
current_collection->export_superstream(s);
|
||||
messages << "Superstream exported." << std::endl;
|
||||
});
|
||||
|
||||
function_ptr_command<const std::string&> load_collection(lsnes_cmd, "load-collection",
|
||||
"Load voice subtitling collection", "load-collection <filename>\nLoad voice subtitling collection "
|
||||
"from <filename>",
|
||||
[](const std::string& x) throw(std::bad_alloc, std::runtime_error) {
|
||||
umutex_class m2(current_collection_lock);
|
||||
filesystem_ref newfs;
|
||||
stream_collection* newc;
|
||||
try {
|
||||
newfs = filesystem_ref(x);
|
||||
newc = new stream_collection(newfs);
|
||||
} catch(std::exception& e) {
|
||||
messages << "Can't load '" << x << "': " << e.what();
|
||||
return;
|
||||
}
|
||||
if(current_collection)
|
||||
delete current_collection;
|
||||
current_collection = newc;
|
||||
messages << "Loaded '" << x << "'" << std::endl;
|
||||
});
|
||||
|
||||
function_ptr_command<> unload_collection(lsnes_cmd, "unload-collection", "Unload voice subtitling collection",
|
||||
"unload-collection\nUnload voice subtitling collection",
|
||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||
umutex_class m2(current_collection_lock);
|
||||
if(current_collection)
|
||||
delete current_collection;
|
||||
current_collection = NULL;
|
||||
messages << "Collection unloaded" << std::endl;
|
||||
});
|
||||
|
||||
inverse_key itangent("+tangent", "Movie‣Voice tangent");
|
||||
}
|
||||
|
||||
bool voicesub_collection_loaded()
|
||||
{
|
||||
umutex_class m2(current_collection_lock);
|
||||
|
|
|
@ -156,47 +156,6 @@ namespace
|
|||
inverse_key ienable_sound("enable-sound on", "Sound‣Enable");
|
||||
inverse_key idisable_sound("enable-sound off", "Sound‣Disable");
|
||||
|
||||
function_ptr_command<const std::string&> set_sound_device(lsnes_cmd, "set-sound-device", "Set sound device",
|
||||
"Syntax: set-sound-device <id>\nSet sound device to <id>.\n",
|
||||
[](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
|
||||
if(!audioapi_driver_initialized())
|
||||
throw std::runtime_error("Sound failed to initialize and is disabled");
|
||||
platform::set_sound_device(args);
|
||||
});
|
||||
|
||||
function_ptr_command<> get_sound_devices(lsnes_cmd, "show-sound-devices", "Show sound devices",
|
||||
"Syntax: show-sound-devices\nShow listing of available sound devices\n",
|
||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||
if(!audioapi_driver_initialized())
|
||||
throw std::runtime_error("Sound failed to initialize and is disabled");
|
||||
auto r = audioapi_driver_get_devices();
|
||||
auto s = audioapi_driver_get_device();
|
||||
std::string dname = "unknown";
|
||||
if(r.count(s))
|
||||
dname = r[s];
|
||||
messages << "Detected " << r.size() << " sound output devices." << std::endl;
|
||||
for(auto i : r)
|
||||
messages << "Audio device " << i.first << ": " << i.second << std::endl;
|
||||
messages << "Currently using device " << audioapi_driver_get_device() << " ("
|
||||
<< dname << ")" << std::endl;
|
||||
});
|
||||
|
||||
function_ptr_command<> get_sound_status(lsnes_cmd, "show-sound-status", "Show sound status",
|
||||
"Syntax: show-sound-status\nShow current sound status\n",
|
||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||
messages << "Sound plugin: " << audioapi_driver_name << std::endl;
|
||||
if(!audioapi_driver_initialized())
|
||||
messages << "Sound initialization failed, sound disabled" << std::endl;
|
||||
else {
|
||||
auto r = audioapi_driver_get_devices();
|
||||
auto s = audioapi_driver_get_device();
|
||||
std::string dname = "unknown";
|
||||
if(r.count(s))
|
||||
dname = r[s];
|
||||
messages << "Current sound device " << s << " (" << dname << ")" << std::endl;
|
||||
}
|
||||
});
|
||||
|
||||
function_ptr_command<const std::string&> set_volume(lsnes_cmd, "set-volume", "Set sound volume",
|
||||
"Syntax: set-volume <scale>\nset-volume <scale>%\nset-volume <scale>dB\nSet sound volume\n",
|
||||
[](const std::string& value) throw(std::bad_alloc, std::runtime_error) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
PLATFORMS=dummy evdev portaudio sdl wxwidgets win32mm libao
|
||||
PLATFORMS=dummy evdev portaudio wxwidgets win32mm libao
|
||||
ALLOBJECT=__all__.$(OBJECT_SUFFIX)
|
||||
ALLFLAGS=__all__.ldflags
|
||||
PLATFORMS_OBJS=$(patsubst %,%/$(ALLOBJECT),$(PLATFORMS))
|
||||
|
@ -23,9 +23,6 @@ portaudio/$(ALLOBJECT): forcelook
|
|||
libao/$(ALLOBJECT): forcelook
|
||||
$(MAKE) -C libao
|
||||
|
||||
sdl/$(ALLOBJECT): forcelook
|
||||
$(MAKE) -C sdl
|
||||
|
||||
wxwidgets/$(ALLOBJECT): forcelook
|
||||
$(MAKE) -C wxwidgets
|
||||
|
||||
|
@ -38,7 +35,6 @@ precheck:
|
|||
$(MAKE) -C win32mm precheck
|
||||
$(MAKE) -C portaudio precheck
|
||||
$(MAKE) -C libao precheck
|
||||
$(MAKE) -C sdl precheck
|
||||
$(MAKE) -C wxwidgets precheck
|
||||
|
||||
clean:
|
||||
|
@ -48,7 +44,6 @@ clean:
|
|||
$(MAKE) -C win32mm clean
|
||||
$(MAKE) -C portaudio clean
|
||||
$(MAKE) -C libao clean
|
||||
$(MAKE) -C sdl clean
|
||||
$(MAKE) -C wxwidgets clean
|
||||
|
||||
forcelook:
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
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) savesettings.$(OBJECT_SUFFIX)
|
||||
SDL_CFLAGS += $(shell $(CROSS_PREFIX)sdl-config --cflags)
|
||||
SDL_LDFLAGS += $(shell $(CROSS_PREFIX)sdl-config --libs)
|
||||
else
|
||||
OBJECTS = dummy.$(OBJECT_SUFFIX)
|
||||
endif
|
||||
|
||||
ifeq ($(SOUND), SDL)
|
||||
ifneq ($(GRAPHICS), SDL)
|
||||
$(error "SDL sound requires SDL graphics")
|
||||
endif
|
||||
OBJECTS += sound.$(OBJECT_SUFFIX)
|
||||
endif
|
||||
|
||||
ifeq ($(JOYSTICK), SDL)
|
||||
ifneq ($(GRAPHICS), SDL)
|
||||
$(error "SDL joystick requires SDL graphics")
|
||||
endif
|
||||
OBJECTS += joystick.$(OBJECT_SUFFIX)
|
||||
else
|
||||
SDL_CFLAGS += -DSDL_NO_JOYSTICK
|
||||
endif
|
||||
|
||||
.PRECIOUS: %.$(OBJECT_SUFFIX)
|
||||
|
||||
__all__.$(OBJECT_SUFFIX): $(OBJECTS)
|
||||
$(REALLD) -r -o $@ $^
|
||||
echo $(SDL_LDFLAGS) >__all__.ldflags
|
||||
|
||||
%.$(OBJECT_SUFFIX): %.cpp
|
||||
$(REALCC) -c -o $@ $< -I../../../include $(CFLAGS) $(SDL_CFLAGS)
|
||||
|
||||
precheck:
|
||||
@true
|
||||
|
||||
clean:
|
||||
rm -f *.$(OBJECT_SUFFIX) *.ldflags
|
|
@ -1,328 +0,0 @@
|
|||
#include "platform/sdl/platform.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#define MAXHISTORY 1000
|
||||
|
||||
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()
|
||||
{
|
||||
enabled_flag = false;
|
||||
autorepeating_key = SPECIAL_NOOP;
|
||||
autorepeat_phase = 0;
|
||||
cursor_pos = 0;
|
||||
overwrite_mode = false;
|
||||
}
|
||||
|
||||
std::string commandline_model::key(uint32_t k) throw(std::bad_alloc)
|
||||
{
|
||||
std::string ret;
|
||||
switch(k) {
|
||||
case SPECIAL_NOOP:
|
||||
return "";
|
||||
case SPECIAL_ACK:
|
||||
history_itr = history.end();
|
||||
history.push_back(codepoints);
|
||||
if(history.size() > MAXHISTORY)
|
||||
history.pop_front();
|
||||
ret = read_command();
|
||||
enabled_flag = false;
|
||||
return ret;
|
||||
case SPECIAL_NAK:
|
||||
enabled_flag = false;
|
||||
return "";
|
||||
default:
|
||||
//Fall through.
|
||||
;
|
||||
};
|
||||
|
||||
//Set up autorepeat if needed.
|
||||
if(k & PRESSED_MASK) {
|
||||
autorepeating_key = k;
|
||||
autorepeat_counter = 0;
|
||||
autorepeat_phase = 1;
|
||||
} else {
|
||||
//These all are active on positive edge.
|
||||
autorepeating_key = SPECIAL_NOOP;
|
||||
autorepeat_counter = 0;
|
||||
autorepeat_phase = 0;
|
||||
return "";
|
||||
}
|
||||
|
||||
switch(k & ~PRESSED_MASK) {
|
||||
case SPECIAL_INSERT:
|
||||
overwrite_mode = !overwrite_mode;
|
||||
case SPECIAL_HOME:
|
||||
cursor_pos = 0;
|
||||
return "";
|
||||
case SPECIAL_END:
|
||||
cursor_pos = codepoints.size();
|
||||
return "";
|
||||
case SPECIAL_PGUP:
|
||||
case SPECIAL_UP:
|
||||
scroll_history_up();
|
||||
return "";
|
||||
case SPECIAL_PGDN:
|
||||
case SPECIAL_DOWN:
|
||||
scroll_history_down();
|
||||
return "";
|
||||
case SPECIAL_LEFT:
|
||||
if(cursor_pos > 0)
|
||||
cursor_pos--;
|
||||
return "";
|
||||
case SPECIAL_RIGHT:
|
||||
if(cursor_pos < codepoints.size())
|
||||
cursor_pos++;
|
||||
return "";
|
||||
case SPECIAL_BACKSPACE:
|
||||
if(cursor_pos == 0)
|
||||
//Nothing to delete.
|
||||
return "";
|
||||
delete_codepoint(cursor_pos - 1);
|
||||
cursor_pos--;
|
||||
return "";
|
||||
case SPECIAL_DELETE:
|
||||
if(cursor_pos == codepoints.size())
|
||||
//Nothing to delete.
|
||||
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?
|
||||
return "";
|
||||
default:
|
||||
//This can't be NOOP, ACK nor NAK because those were checked above, nor is it special. Therefore
|
||||
//it is a character.
|
||||
handle_cow();
|
||||
if(!overwrite_mode || cursor_pos == codepoints.size()) {
|
||||
codepoints.resize(codepoints.size() + 1);
|
||||
for(size_t i = codepoints.size() - 1; i > cursor_pos; i--)
|
||||
codepoints[i] = codepoints[i - 1];
|
||||
}
|
||||
codepoints[cursor_pos++] = k & ~PRESSED_MASK;
|
||||
return "";
|
||||
};
|
||||
}
|
||||
|
||||
void commandline_model::tick() throw(std::bad_alloc)
|
||||
{
|
||||
if(autorepeat_phase == 0 || !enabled_flag)
|
||||
return;
|
||||
if(autorepeat_phase == 1) {
|
||||
autorepeat_counter++;
|
||||
if(autorepeat_counter >= autorepeat_first) {
|
||||
key(autorepeating_key);
|
||||
autorepeat_phase = 2;
|
||||
autorepeat_counter = 0;
|
||||
}
|
||||
}
|
||||
if(autorepeat_phase == 2) {
|
||||
autorepeat_counter++;
|
||||
if(autorepeat_counter >= autorepeat_subsequent) {
|
||||
key(autorepeating_key);
|
||||
autorepeat_counter = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t commandline_model::cursor() throw()
|
||||
{
|
||||
return enabled_flag ? cursor_pos : 0;
|
||||
}
|
||||
|
||||
bool commandline_model::enabled() throw()
|
||||
{
|
||||
return enabled_flag;
|
||||
}
|
||||
|
||||
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)
|
||||
return;
|
||||
enabled_flag = true;
|
||||
autorepeat_phase = 0;
|
||||
autorepeat_counter = 0;
|
||||
codepoints.clear();
|
||||
cursor_pos = 0;
|
||||
overwrite_mode = false;
|
||||
history_itr = history.end();
|
||||
}
|
||||
|
||||
std::string commandline_model::read_command() throw(std::bad_alloc)
|
||||
{
|
||||
std::ostringstream o;
|
||||
for(auto i : codepoints) {
|
||||
char buf[5] = {0, 0, 0, 0, 0};
|
||||
if(i < 0x80)
|
||||
buf[0] = i;
|
||||
else if(i < 0x800) {
|
||||
buf[0] = 0xC0 | (i >> 6);
|
||||
buf[1] = 0x80 | (i & 63);
|
||||
} else if(i < 0x10000) {
|
||||
buf[0] = 0xE0 | (i >> 12);
|
||||
buf[1] = 0x80 | ((i >> 6) & 63);
|
||||
buf[2] = 0x80 | (i & 63);
|
||||
} else if(i < 0x10FFFF) {
|
||||
buf[0] = 0xF0 | (i >> 18);
|
||||
buf[1] = 0x80 | ((i >> 12) & 63);
|
||||
buf[2] = 0x80 | ((i >> 6) & 63);
|
||||
buf[3] = 0x80 | (i & 63);
|
||||
}
|
||||
o << buf;
|
||||
}
|
||||
return o.str();
|
||||
}
|
||||
|
||||
void commandline_model::handle_cow()
|
||||
{
|
||||
if(history_itr == history.end())
|
||||
//Latched to end of history.
|
||||
return;
|
||||
codepoints = *history_itr;
|
||||
history_itr = history.end();
|
||||
}
|
||||
|
||||
void commandline_model::scroll_history_up()
|
||||
{
|
||||
if(history_itr == history.end())
|
||||
//Save the current commandline.
|
||||
saved_codepoints = codepoints;
|
||||
if(history_itr == history.begin())
|
||||
//At the begnning.
|
||||
return;
|
||||
history_itr--;
|
||||
codepoints = *history_itr;
|
||||
if(cursor_pos > codepoints.size())
|
||||
cursor_pos = codepoints.size();
|
||||
}
|
||||
|
||||
void commandline_model::scroll_history_down()
|
||||
{
|
||||
if(history_itr == history.end())
|
||||
//At the end.
|
||||
return;
|
||||
history_itr++;
|
||||
if(history_itr == history.end())
|
||||
codepoints = saved_codepoints;
|
||||
else
|
||||
codepoints = *history_itr;
|
||||
if(cursor_pos > codepoints.size())
|
||||
cursor_pos = codepoints.size();
|
||||
}
|
||||
|
||||
void commandline_model::delete_codepoint(size_t idx)
|
||||
{
|
||||
handle_cow();
|
||||
for(size_t i = idx; i < codepoints.size() - 1; i++)
|
||||
codepoints[i] = codepoints[i + 1];
|
||||
codepoints.resize(codepoints.size() - 1);
|
||||
}
|
||||
|
||||
void commandline_model::paint(SDL_Surface* surf, uint32_t x, uint32_t y, uint32_t maxwidth, uint32_t color,
|
||||
bool box, uint32_t boxcolor) throw()
|
||||
{
|
||||
if(!maxwidth)
|
||||
return;
|
||||
try {
|
||||
if(box)
|
||||
draw_box(surf, x, y, maxwidth, 16, boxcolor);
|
||||
if(enabled_flag)
|
||||
draw_string(surf, read_command(), x, y, maxwidth, color, overwrite_mode ? 2 : 1, cursor_pos);
|
||||
else
|
||||
draw_string(surf, "", x, y, maxwidth, color, 0, 0);
|
||||
} catch(...) {
|
||||
OOM_panic();
|
||||
}
|
||||
}
|
|
@ -1,320 +0,0 @@
|
|||
#include "core/misc.hpp"
|
||||
#include "fonts/wrapper.hpp"
|
||||
#include "platform/sdl/platform.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
||||
#define WRITE3(ptr, idx, c) do {\
|
||||
(ptr)[(idx) + 0] = (c) >> 16; \
|
||||
(ptr)[(idx) + 1] = (c) >> 8; \
|
||||
(ptr)[(idx) + 2] = (c); \
|
||||
} while(0)
|
||||
#else
|
||||
#define WRITE3(ptr, idx, c) do {\
|
||||
(ptr)[(idx) + 0] = (c); \
|
||||
(ptr)[(idx) + 1] = (c) >> 8; \
|
||||
(ptr)[(idx) + 2] = (c) >> 16; \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
std::vector<uint32_t> decode_utf8(std::string s)
|
||||
{
|
||||
std::vector<uint32_t> ret;
|
||||
for(auto i = s.begin(); i != s.end(); i++) {
|
||||
uint32_t j = static_cast<uint8_t>(*i);
|
||||
if(j < 128)
|
||||
ret.push_back(j);
|
||||
else if(j < 192)
|
||||
continue;
|
||||
else if(j < 224) {
|
||||
uint32_t j2 = static_cast<uint8_t>(*(++i));
|
||||
ret.push_back((j - 192) * 64 + (j2 - 128));
|
||||
} else if(j < 240) {
|
||||
uint32_t j2 = static_cast<uint8_t>(*(++i));
|
||||
uint32_t j3 = static_cast<uint8_t>(*(++i));
|
||||
ret.push_back((j - 224) * 4096 + (j2 - 128) * 64 + (j3 - 128));
|
||||
} else {
|
||||
uint32_t j2 = static_cast<uint8_t>(*(++i));
|
||||
uint32_t j3 = static_cast<uint8_t>(*(++i));
|
||||
uint32_t j4 = static_cast<uint8_t>(*(++i));
|
||||
ret.push_back((j - 240) * 262144 + (j2 - 128) * 4096 + (j3 - 128) * 64 + (j4 - 128));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
inline void draw_blank_glyph_3(uint8_t* base, uint32_t pitch, uint32_t w, uint32_t color, uint32_t curstart)
|
||||
{
|
||||
for(uint32_t j = 0; j < 16; j++) {
|
||||
uint8_t* ptr = base + j * pitch;
|
||||
uint32_t c = (j >= curstart) ? color : 0;
|
||||
for(uint32_t i = 0; i < w; i++)
|
||||
WRITE3(ptr, 3 * i, c);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void draw_blank_glyph_T(uint8_t* base, uint32_t pitch, uint32_t w, uint32_t color, uint32_t curstart)
|
||||
{
|
||||
for(uint32_t j = 0; j < 16; j++) {
|
||||
T* ptr = reinterpret_cast<T*>(base + j * pitch);
|
||||
T c = (j >= curstart) ? color : 0;
|
||||
for(uint32_t i = 0; i < w; i++)
|
||||
ptr[i] = c;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool wide>
|
||||
inline void draw_glyph_3(uint8_t* base, uint32_t pitch, uint32_t w, const uint32_t* gdata, uint32_t color,
|
||||
uint32_t curstart)
|
||||
{
|
||||
for(uint32_t j = 0; j < 16; j++) {
|
||||
uint8_t* ptr = base + j * pitch;
|
||||
uint32_t bgc = (j >= curstart) ? color : 0;
|
||||
uint32_t fgc = (j >= curstart) ? 0 : color;
|
||||
uint32_t dataword = gdata[j >> (wide ? 1 : 2)];
|
||||
unsigned rbit = (~(j << (wide ? 4 : 3))) & 0x1F;
|
||||
for(uint32_t i = 0; i < w; i++) {
|
||||
bool b = (((dataword >> (rbit - i)) & 1));
|
||||
WRITE3(ptr, 3 * i, b ? fgc : bgc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, bool wide>
|
||||
inline void draw_glyph_T(uint8_t* base, uint32_t pitch, uint32_t w, const uint32_t* gdata, uint32_t color,
|
||||
uint32_t curstart)
|
||||
{
|
||||
for(uint32_t j = 0; j < 16; j++) {
|
||||
T* ptr = reinterpret_cast<T*>(base + j * pitch);
|
||||
T bgc = (j >= curstart) ? color : 0;
|
||||
T fgc = (j >= curstart) ? 0 : color;
|
||||
uint32_t dataword = gdata[j >> (wide ? 1 : 2)];
|
||||
unsigned rbit = (~(j << (wide ? 4 : 3))) & 0x1F;
|
||||
for(uint32_t i = 0; i < w; i++) {
|
||||
bool b = (((dataword >> (rbit - i)) & 1));
|
||||
ptr[i] = b ? fgc : bgc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_blank_glyph(uint8_t* base, uint32_t pitch, uint32_t pbytes, uint32_t w, uint32_t wleft,
|
||||
uint32_t color, uint32_t hilite_mode)
|
||||
{
|
||||
if(w > wleft)
|
||||
w = wleft;
|
||||
if(!w)
|
||||
return;
|
||||
uint32_t curstart = 16;
|
||||
if(hilite_mode == 1)
|
||||
curstart = 14;
|
||||
if(hilite_mode == 2)
|
||||
curstart = 0;
|
||||
switch(pbytes) {
|
||||
case 1:
|
||||
draw_blank_glyph_T<uint8_t>(base, pitch, w, color, curstart);
|
||||
break;
|
||||
case 2:
|
||||
draw_blank_glyph_T<uint16_t>(base, pitch, w, color, curstart);
|
||||
break;
|
||||
case 3:
|
||||
draw_blank_glyph_3(base, pitch, w, color, curstart);
|
||||
break;
|
||||
case 4:
|
||||
draw_blank_glyph_T<uint32_t>(base, pitch, w, color, curstart);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void draw_glyph(uint8_t* base, uint32_t pitch, uint32_t pbytes, const uint32_t* gdata, uint32_t w,
|
||||
uint32_t wleft, uint32_t color, uint32_t hilite_mode)
|
||||
{
|
||||
bool wide = (w > 8);
|
||||
if(w > wleft)
|
||||
w = wleft;
|
||||
if(!w)
|
||||
return;
|
||||
uint32_t curstart = 16;
|
||||
if(hilite_mode == 1)
|
||||
curstart = 14;
|
||||
if(hilite_mode == 2)
|
||||
curstart = 0;
|
||||
switch(pbytes) {
|
||||
case 1:
|
||||
if(wide)
|
||||
draw_glyph_T<uint8_t, true>(base, pitch, w, gdata, color, curstart);
|
||||
else
|
||||
draw_glyph_T<uint8_t, false>(base, pitch, w, gdata, color, curstart);
|
||||
break;
|
||||
case 2:
|
||||
if(wide)
|
||||
draw_glyph_T<uint16_t, true>(base, pitch, w, gdata, color, curstart);
|
||||
else
|
||||
draw_glyph_T<uint16_t, false>(base, pitch, w, gdata, color, curstart);
|
||||
break;
|
||||
case 3:
|
||||
if(wide)
|
||||
draw_glyph_3<true>(base, pitch, w, gdata, color, curstart);
|
||||
else
|
||||
draw_glyph_3<false>(base, pitch, w, gdata, color, curstart);
|
||||
break;
|
||||
case 4:
|
||||
if(wide)
|
||||
draw_glyph_T<uint32_t, true>(base, pitch, w, gdata, color, curstart);
|
||||
else
|
||||
draw_glyph_T<uint32_t, false>(base, pitch, w, gdata, color, curstart);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void draw_string(uint8_t* base, uint32_t pitch, uint32_t pbytes, std::vector<uint32_t> s, uint32_t x,
|
||||
uint32_t y, uint32_t maxwidth, uint32_t color, uint32_t hilite_mode = 0, uint32_t hilite_pos = 0)
|
||||
{
|
||||
int32_t pos_x = 0;
|
||||
int32_t pos_y = 0;
|
||||
size_t xo = pbytes;
|
||||
size_t yo = pitch;
|
||||
unsigned c = 0;
|
||||
for(auto si : s) {
|
||||
uint32_t old_x = pos_x;
|
||||
uint32_t old_y = pos_y;
|
||||
auto g = main_font.get_glyph(si);
|
||||
if(si == 9)
|
||||
pos_x = (pos_x + 63) / 64 * 64;
|
||||
else if(si == 10) {
|
||||
pos_x = 0;
|
||||
pos_y += 16;
|
||||
} else
|
||||
pos_x += (g.wide ? 16 : 8);
|
||||
uint32_t mw = maxwidth - old_x;
|
||||
if(maxwidth < old_x)
|
||||
mw = 0;
|
||||
if(mw > (g.wide ? 16 : 8))
|
||||
mw = (g.wide ? 16 : 8);
|
||||
uint8_t* cbase = base + (y + old_y) * yo + (x + old_x) * xo;
|
||||
if(g.data == NULL)
|
||||
draw_blank_glyph(cbase, pitch, pbytes, (g.wide ? 16 : 8), mw, color,
|
||||
(c == hilite_pos) ? hilite_mode : 0);
|
||||
else
|
||||
draw_glyph(cbase, pitch, pbytes, g.data, (g.wide ? 16 : 8), mw, color,
|
||||
(c == hilite_pos) ? hilite_mode : 0);
|
||||
c++;
|
||||
}
|
||||
if(c == hilite_pos) {
|
||||
uint32_t old_x = pos_x;
|
||||
uint32_t mw = maxwidth - old_x;
|
||||
if(maxwidth < old_x)
|
||||
mw = 0;
|
||||
draw_blank_glyph(base + y * yo + (x + old_x) * xo, pitch, pbytes, 8, mw, 0xFFFFFFFFU,
|
||||
hilite_mode);
|
||||
pos_x += 8;
|
||||
}
|
||||
if(pos_x < maxwidth)
|
||||
draw_blank_glyph(base + y * yo + (x + pos_x) * xo, pitch, pbytes, maxwidth - pos_x,
|
||||
maxwidth - pos_x, 0, 0);
|
||||
}
|
||||
|
||||
void paint_line(uint8_t* ptr, uint32_t length, uint32_t step, uint32_t pbytes, uint32_t color)
|
||||
{
|
||||
switch(pbytes) {
|
||||
case 1:
|
||||
for(uint32_t i = 0; i < length; i++) {
|
||||
*ptr = color;
|
||||
ptr += step;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
for(uint32_t i = 0; i < length; i++) {
|
||||
*reinterpret_cast<uint16_t*>(ptr) = color;
|
||||
ptr += step;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
for(uint32_t i = 0; i < length; i++) {
|
||||
WRITE3(ptr, 0, color);
|
||||
ptr += step;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
for(uint32_t i = 0; i < length; i++) {
|
||||
*reinterpret_cast<uint32_t*>(ptr) = color;
|
||||
ptr += step;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_string(uint8_t* base, uint32_t pitch, uint32_t pbytes, std::string s, uint32_t x, uint32_t y,
|
||||
uint32_t maxwidth, uint32_t color, uint32_t hilite_mode, uint32_t hilite_pos) throw()
|
||||
{
|
||||
try {
|
||||
draw_string(base, pitch, pbytes, decode_utf8(s), x, y, maxwidth, color, hilite_mode, hilite_pos);
|
||||
} catch(...) {
|
||||
OOM_panic();
|
||||
}
|
||||
}
|
||||
|
||||
void draw_string(SDL_Surface* surf, std::string s, uint32_t x, uint32_t y, uint32_t maxwidth, uint32_t color,
|
||||
uint32_t hilite_mode, uint32_t hilite_pos) throw()
|
||||
{
|
||||
try {
|
||||
draw_string(reinterpret_cast<uint8_t*>(surf->pixels), surf->pitch, surf->format->BytesPerPixel,
|
||||
decode_utf8(s), x, y, maxwidth, color, hilite_mode, hilite_pos);
|
||||
} catch(...) {
|
||||
OOM_panic();
|
||||
}
|
||||
}
|
||||
|
||||
void draw_box(uint8_t* base, uint32_t pitch, uint32_t pbytes, uint32_t width, uint32_t height, uint32_t x, uint32_t y,
|
||||
uint32_t w, uint32_t h, uint32_t color) throw()
|
||||
{
|
||||
if(!w || !h)
|
||||
return;
|
||||
uint32_t sx, sy, ex, ey;
|
||||
uint32_t bsx, bsy, bex, bey;
|
||||
uint32_t lstride = pitch;
|
||||
uint8_t* p = base;
|
||||
size_t xo = pbytes;
|
||||
size_t yo = pitch;
|
||||
sx = (x < 6) ? 0 : (x - 6);
|
||||
sy = (y < 6) ? 0 : (y - 6);
|
||||
ex = (x + w + 6 > width) ? width : (x + w + 6);
|
||||
ey = (y + h + 6 > height) ? height : (y + h + 6);
|
||||
bsx = (x < 4) ? 0 : (x - 4);
|
||||
bsy = (y < 4) ? 0 : (y - 4);
|
||||
bex = (x + w + 4 > width) ? width : (x + w + 4);
|
||||
bey = (y + h + 4 > height) ? height : (y + h + 4);
|
||||
//First, blank the area.
|
||||
for(uint32_t j = sy; j < ey; j++)
|
||||
memset(p + j * yo + sx * xo, 0, (ex - sx) * xo);
|
||||
//Paint the borders.
|
||||
if(x >= 4)
|
||||
paint_line(p + bsy * yo + (x - 4) * xo, bey - bsy, yo, xo, color);
|
||||
if(x >= 3)
|
||||
paint_line(p + bsy * yo + (x - 3) * xo, bey - bsy, yo, xo, color);
|
||||
if(y >= 4)
|
||||
paint_line(p + (y - 4) * yo + bsx * xo, bex - bsx, xo, xo, color);
|
||||
if(y >= 3)
|
||||
paint_line(p + (y - 3) * yo + bsx * xo, bex - bsx, xo, xo, color);
|
||||
if(x + w + 3 < width)
|
||||
paint_line(p + bsy * yo + (x + w + 2) * xo, bey - bsy, yo, xo, color);
|
||||
if(x + w + 4 < width)
|
||||
paint_line(p + bsy * yo + (x + w + 3) * xo, bey - bsy, yo, xo, color);
|
||||
if(y + h + 3 < height)
|
||||
paint_line(p + (y + h + 2) * yo + bsx * xo, bex - bsx, xo, xo, color);
|
||||
if(y + h + 4 < height)
|
||||
paint_line(p + (y + h + 3) * yo + bsx * xo, bex - bsx, xo, xo, color);
|
||||
}
|
||||
|
||||
void draw_box(SDL_Surface* surf, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t color) throw()
|
||||
{
|
||||
draw_box(reinterpret_cast<uint8_t*>(surf->pixels), surf->pitch, surf->format->BytesPerPixel, surf->w, surf->h,
|
||||
x, y, w, h, color);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
char SYMBOL_1243228960869283359802095839623673287633;
|
|
@ -1,617 +0,0 @@
|
|||
#include "lsnes.hpp"
|
||||
#include "core/command.hpp"
|
||||
#include "core/dispatch.hpp"
|
||||
#include "core/framebuffer.hpp"
|
||||
#include "core/framerate.hpp"
|
||||
#include "core/window.hpp"
|
||||
#include "platform/sdl/platform.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
screen_model* screenmod;
|
||||
|
||||
#define USERCODE_TIMER 0
|
||||
#define USERCODE_PAINT 1
|
||||
|
||||
#define SPECIALMODE_NORMAL 0
|
||||
#define SPECIALMODE_COMMAND 1
|
||||
#define SPECIALMODE_IDENTIFY 2
|
||||
#define SPECIALMODE_MODAL 3
|
||||
|
||||
#ifdef SDL_NO_JOYSTICK
|
||||
unsigned translate_sdl_joystick(SDL_Event& e, keypress& k1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
//Dirty flags for various displays.
|
||||
volatile bool messages_dirty = false;
|
||||
volatile bool status_dirty = false;
|
||||
volatile bool screen_dirty = false;
|
||||
volatile bool fullscreen_console = false;
|
||||
bool fullscreen_console_active = false;
|
||||
//If true, repaint request is in flight. Protected by ui_mutex.
|
||||
volatile bool repaint_in_flight = false;
|
||||
//If true, timer event has occured.
|
||||
volatile bool timer_triggered = false;
|
||||
//If true, modal dialog is to be displayed. Pull low to ack the dialog. Protected by ui_mutex.
|
||||
volatile bool modal_dialog_active = false;
|
||||
//If modal_dialog_active is true, this is the text for the dialog box. Protected by ui_mutex.
|
||||
std::string modal_dialog_text;
|
||||
//If true, the modal dialog is confirmation dialog. Pull low if confirmation dialog is canceled. Protected
|
||||
//by ui_mutex.
|
||||
volatile bool modal_dialog_confirm = true;
|
||||
//Set if emulator panics.
|
||||
volatile bool paniced = false;
|
||||
//Set when user dismisses panic prompt (emulator can exit).
|
||||
volatile bool panic_ack = false;
|
||||
//Set after SIGALRM handler has got installed.
|
||||
bool sigalrm_handler_installed = false;
|
||||
//Special mode.
|
||||
unsigned special_mode;
|
||||
volatile bool identify_requested;
|
||||
volatile bool emulator_thread_exited = false;
|
||||
//The command line modal to use.
|
||||
commandline_model cmdline;
|
||||
//Mutex protecting various variables and associated mutex for waking the emulator thread from some blocking
|
||||
//operations.
|
||||
mutex* ui_mutex;
|
||||
condition* ui_condition;
|
||||
//Set to true when SDL has been initialized.
|
||||
bool sdl_init = false;
|
||||
//Thread ID of UI thread (for identifying it).
|
||||
thread_id* ui_thread;
|
||||
//Timer for implementing stuff like autorepeat.
|
||||
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)
|
||||
{
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
Uint32 timer_cb(Uint32 interval, void* param)
|
||||
{
|
||||
timer_triggered = true;
|
||||
timer_irq_counter = timer_irq_counter + 1;
|
||||
return interval;
|
||||
}
|
||||
|
||||
void arm_sigalrm()
|
||||
{
|
||||
#ifdef SIGALRM
|
||||
if(!sigalrm_handler_installed)
|
||||
signal(SIGALRM, sigalrm_handler);
|
||||
sigalrm_handler_installed = true;
|
||||
//alarm(15);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ui_panic()
|
||||
{
|
||||
//Be very careful what you use here, as program state can be really unpredictable!
|
||||
try {
|
||||
platform::message("PANIC: Cannot continue, press ESC or close window to exit.");
|
||||
screenmod->repaint_full();
|
||||
screenmod->flip();
|
||||
} catch(...) {
|
||||
//Just crash.
|
||||
panic_ack = true;
|
||||
return;
|
||||
}
|
||||
while(true) {
|
||||
SDL_Event e;
|
||||
if(SDL_WaitEvent(&e)) {
|
||||
if(e.type == SDL_QUIT)
|
||||
break;
|
||||
if(e.type == SDL_ACTIVEEVENT) {
|
||||
screenmod->repaint_full();
|
||||
screenmod->flip();
|
||||
}
|
||||
if(e.type == SDL_KEYUP && e.key.keysym.sym == SDLK_ESCAPE)
|
||||
break;
|
||||
}
|
||||
}
|
||||
panic_ack = true;
|
||||
}
|
||||
|
||||
void wake_ui()
|
||||
{
|
||||
mutex::holder h(*ui_mutex);
|
||||
if(!repaint_in_flight) {
|
||||
//Wake the UI.
|
||||
repaint_in_flight = true;
|
||||
}
|
||||
}
|
||||
|
||||
function_ptr_command<> identify_key(lsnes_cmd, "identify-key", "Identify a key",
|
||||
"Syntax: identify-key\nIdentifies a (pseudo-)key.\n",
|
||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||
identify_requested = true;
|
||||
wake_ui();
|
||||
});
|
||||
|
||||
function_ptr_command<> scroll_up(lsnes_cmd, "scroll-up", "Scroll messages a page up",
|
||||
"Syntax: scroll-up\nScrolls message console backward one page.\n",
|
||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||
mutex::holder h(platform::msgbuf_lock());
|
||||
platform::msgbuf.scroll_up_page();
|
||||
});
|
||||
|
||||
function_ptr_command<> scroll_fullup(lsnes_cmd, "scroll-fullup", "Scroll messages to beginning",
|
||||
"Syntax: scroll-fullup\nScrolls message console to its beginning.\n",
|
||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||
mutex::holder h(platform::msgbuf_lock());
|
||||
platform::msgbuf.scroll_beginning();
|
||||
});
|
||||
|
||||
function_ptr_command<> scroll_fulldown(lsnes_cmd, "scroll-fulldown", "Scroll messages to end",
|
||||
"Syntax: scroll-fulldown\nScrolls message console to its end.\n",
|
||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||
mutex::holder h(platform::msgbuf_lock());
|
||||
platform::msgbuf.scroll_end();
|
||||
});
|
||||
|
||||
function_ptr_command<> scrolldown(lsnes_cmd, "scroll-down", "Scroll messages a page down",
|
||||
"Syntax: scroll-up\nScrolls message console forward one page.\n",
|
||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||
mutex::holder h(platform::msgbuf_lock());
|
||||
platform::msgbuf.scroll_down_page();
|
||||
});
|
||||
|
||||
function_ptr_command<> toggle_console(lsnes_cmd, "toggle-console", "Toggle console between small and full "
|
||||
"window", "Syntax: toggle-console\nToggles console between small and large.\n",
|
||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||
fullscreen_console = !fullscreen_console;
|
||||
wake_ui();
|
||||
});
|
||||
|
||||
class keygrabber : public keyboard_event_listener
|
||||
{
|
||||
public:
|
||||
keygrabber() { idmode = false; }
|
||||
void enter_id_mode()
|
||||
{
|
||||
keys = "";
|
||||
idmode = true;
|
||||
}
|
||||
bool got_id()
|
||||
{
|
||||
return keys != "";
|
||||
}
|
||||
std::string get_id()
|
||||
{
|
||||
return keys;
|
||||
}
|
||||
void leave_id_mode()
|
||||
{
|
||||
keys = "";
|
||||
idmode = false;
|
||||
}
|
||||
void on_key_event(keyboard_modifier_set& mods, keyboard_key& key, keyboard_event& event)
|
||||
{
|
||||
uint32_t dev = event.get_change_mask();
|
||||
auto subkeys = key.get_subkeys();
|
||||
for(unsigned i = 0; i < 16 && i < subkeys.size(); i++)
|
||||
if(idmode && ((dev >> (2 * i)) & 3) == 2)
|
||||
keys = keys + "key: " + key.get_name() + subkeys[i] + "\n";
|
||||
}
|
||||
bool idmode;
|
||||
std::string keys;
|
||||
} keygrabber;
|
||||
|
||||
void emu_ungrab_keys(void* dummy)
|
||||
{
|
||||
lsnes_kbd.set_exclusive(NULL);
|
||||
keygrabber.leave_id_mode();
|
||||
}
|
||||
|
||||
void emu_grab_keys_identify(void* dummy)
|
||||
{
|
||||
keygrabber.enter_id_mode();
|
||||
lsnes_kbd.set_exclusive(&keygrabber);
|
||||
}
|
||||
|
||||
void emu_grab_keys_nonid(void* dummy)
|
||||
{
|
||||
keygrabber.leave_id_mode();
|
||||
lsnes_kbd.set_exclusive(&keygrabber);
|
||||
}
|
||||
|
||||
void emu_handle_quit_signal(void* dummy)
|
||||
{
|
||||
information_dispatch::do_close();
|
||||
}
|
||||
|
||||
void emu_handle_identify(void* dummy)
|
||||
{
|
||||
if(keygrabber.got_id()) {
|
||||
std::string k = keygrabber.get_id();
|
||||
keygrabber.leave_id_mode();
|
||||
platform::modal_message(k, false);
|
||||
}
|
||||
//Exiting the modal mode undoes key grab and modal pause.
|
||||
}
|
||||
|
||||
//Grab keys, setting or unsetting id mode.
|
||||
void ui_grab_keys(bool idmode)
|
||||
{
|
||||
if(idmode)
|
||||
platform::queue(emu_grab_keys_identify, NULL, true);
|
||||
else
|
||||
platform::queue(emu_grab_keys_nonid, NULL, true);
|
||||
}
|
||||
|
||||
//Grab keys, unset id mode, special.
|
||||
void ui_grab_keys_special()
|
||||
{
|
||||
keygrabber.leave_id_mode();
|
||||
lsnes_kbd.set_exclusive(&keygrabber);
|
||||
}
|
||||
|
||||
//Ungrab keys.
|
||||
void ui_ungrab_keys(bool direct = false)
|
||||
{
|
||||
if(direct) {
|
||||
lsnes_kbd.set_exclusive(NULL);
|
||||
keygrabber.leave_id_mode();
|
||||
} else
|
||||
platform::queue(emu_ungrab_keys, NULL, true);
|
||||
}
|
||||
|
||||
//Handle identify timer interrupt.
|
||||
void ui_handle_identify()
|
||||
{
|
||||
//This call has to be asynchronous.
|
||||
platform::queue(emu_handle_identify, NULL, false);
|
||||
}
|
||||
|
||||
function_ptr_command<const std::string&> exec_command_prefix(lsnes_cmd, "prompt-command",
|
||||
"Prompt for command", "Syntax: prompt-command <prefix>\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()
|
||||
{
|
||||
platform::queue(emu_handle_quit_signal, NULL, false);
|
||||
}
|
||||
|
||||
keyboard_mouse_calibration mouse_cal = {0};
|
||||
keyboard_key_mouse mouse_x(lsnes_kbd, "mouse_x", "mouse", mouse_cal);
|
||||
keyboard_key_mouse mouse_y(lsnes_kbd, "mouse_y", "mouse", mouse_cal);
|
||||
keyboard_key_key mouse_l(lsnes_kbd, "mouse_left", "mouse");
|
||||
keyboard_key_key mouse_m(lsnes_kbd, "mouse_center", "mouse");
|
||||
keyboard_key_key mouse_r(lsnes_kbd, "mouse_right", "mouse");
|
||||
}
|
||||
|
||||
void notify_emulator_exit()
|
||||
{
|
||||
emulator_thread_exited = true;
|
||||
wake_ui();
|
||||
}
|
||||
|
||||
void ui_loop()
|
||||
{
|
||||
uint32_t mouse_state = 0;
|
||||
uint32_t k;
|
||||
uint64_t kts;
|
||||
std::string cmd;
|
||||
bool modal_dialog_was_active = false;
|
||||
while(!emulator_thread_exited) {
|
||||
bool commandline_updated = false;
|
||||
SDL_Event e;
|
||||
SDLKey kbdkey;
|
||||
bool iskbd = false;
|
||||
bool polarity;
|
||||
bool full = false;
|
||||
memset(&e, 0, sizeof(e));
|
||||
{
|
||||
ui_mutex->lock();
|
||||
int r = SDL_PollEvent(&e);
|
||||
if(!repaint_in_flight && !timer_triggered && !delayed_cmd_active && !r) {
|
||||
ui_mutex->unlock();
|
||||
usleep(2000);
|
||||
continue;
|
||||
}
|
||||
ui_mutex->unlock();
|
||||
}
|
||||
if(e.type == SDL_KEYUP) {
|
||||
iskbd = true;
|
||||
polarity = false;
|
||||
kbdkey = e.key.keysym.sym;
|
||||
//std::cerr << "Keyup symbol " << kbdkey << "(held=" << (get_utime() - kts) << "us)" << std::endl;
|
||||
} else if(e.type == SDL_KEYDOWN) {
|
||||
iskbd = true;
|
||||
polarity = true;
|
||||
kbdkey = e.key.keysym.sym;
|
||||
//std::cerr << "Keydown symbol " << kbdkey << std::endl;
|
||||
kts = get_utime();
|
||||
}
|
||||
if(e.type == SDL_VIDEOEXPOSE || e.type == SDL_ACTIVEEVENT)
|
||||
full = true;
|
||||
//Handle panics.
|
||||
if(paniced) {
|
||||
ui_panic();
|
||||
while(true);
|
||||
}
|
||||
if(e.type == SDL_MOUSEMOTION && special_mode == SPECIALMODE_NORMAL) {
|
||||
platform::queue(keypress(keyboard_modifier_set(), mouse_x, e.motion.x - 6));
|
||||
platform::queue(keypress(keyboard_modifier_set(), mouse_y, e.motion.y - 6));
|
||||
}
|
||||
if(e.type == SDL_MOUSEBUTTONDOWN && special_mode == SPECIALMODE_NORMAL) {
|
||||
int i;
|
||||
platform::queue(keypress(keyboard_modifier_set(), mouse_x, e.button.x - 6));
|
||||
platform::queue(keypress(keyboard_modifier_set(), mouse_y, e.button.y - 6));
|
||||
switch(e.button.button) {
|
||||
case SDL_BUTTON_LEFT:
|
||||
platform::queue(keypress(keyboard_modifier_set(), mouse_l, 1));
|
||||
break;
|
||||
case SDL_BUTTON_MIDDLE:
|
||||
platform::queue(keypress(keyboard_modifier_set(), mouse_m, 1));
|
||||
break;
|
||||
case SDL_BUTTON_RIGHT:
|
||||
platform::queue(keypress(keyboard_modifier_set(), mouse_r, 1));
|
||||
break;
|
||||
};
|
||||
}
|
||||
if(e.type == SDL_MOUSEBUTTONUP && special_mode == SPECIALMODE_NORMAL) {
|
||||
platform::queue(keypress(keyboard_modifier_set(), mouse_x, e.button.x - 6));
|
||||
platform::queue(keypress(keyboard_modifier_set(), mouse_y, e.button.y - 6));
|
||||
switch(e.button.button) {
|
||||
case SDL_BUTTON_LEFT:
|
||||
platform::queue(keypress(keyboard_modifier_set(), mouse_l, 0));
|
||||
break;
|
||||
case SDL_BUTTON_MIDDLE:
|
||||
platform::queue(keypress(keyboard_modifier_set(), mouse_m, 0));
|
||||
break;
|
||||
case SDL_BUTTON_RIGHT:
|
||||
platform::queue(keypress(keyboard_modifier_set(), mouse_r, 0));
|
||||
break;
|
||||
};
|
||||
}
|
||||
//Handle entering identify mode.
|
||||
if(identify_requested) {
|
||||
identify_requested = false;
|
||||
special_mode = SPECIALMODE_IDENTIFY;
|
||||
ui_grab_keys(true);
|
||||
platform::message("Press key to identify...");
|
||||
}
|
||||
//Handle entering modal dialog.
|
||||
if(!modal_dialog_was_active && modal_dialog_active) {
|
||||
screenmod->set_modal(modal_dialog_text, modal_dialog_confirm);
|
||||
special_mode = SPECIALMODE_MODAL;
|
||||
modal_dialog_was_active = true;
|
||||
ui_grab_keys_special();
|
||||
}
|
||||
//Handle special modes.
|
||||
switch(special_mode) {
|
||||
case SPECIALMODE_NORMAL:
|
||||
//Enable command line if needed.
|
||||
if((iskbd && kbdkey == SDLK_ESCAPE) || delayed_cmd_active) {
|
||||
if(!cmdline.enabled() && !polarity) {
|
||||
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);
|
||||
ui_grab_keys(false);
|
||||
//std::cerr << "Entered commandline mode." << std::endl;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case SPECIALMODE_COMMAND:
|
||||
if(timer_triggered || (e.type == SDL_USEREVENT && e.user.code == USERCODE_TIMER)) {
|
||||
cmdline.tick();
|
||||
timer_triggered = false;
|
||||
}
|
||||
cmd = cmdline.key(get_command_edit_operation(e, true));
|
||||
if(cmd != "") {
|
||||
//std::cerr << "To execute: '" << cmd << "'" << std::endl;
|
||||
platform::queue(cmd);
|
||||
}
|
||||
commandline_updated = true;
|
||||
if(!cmdline.enabled()) {
|
||||
//std::cerr << "Exited commandline mode." << std::endl;
|
||||
//Exiting commandline mode.
|
||||
special_mode = SPECIALMODE_NORMAL;
|
||||
platform::set_modal_pause(false);
|
||||
ui_ungrab_keys();
|
||||
}
|
||||
break;
|
||||
case SPECIALMODE_IDENTIFY:
|
||||
if(timer_triggered || (e.type == SDL_USEREVENT && e.user.code == USERCODE_TIMER)) {
|
||||
ui_handle_identify();
|
||||
timer_triggered = false;
|
||||
}
|
||||
break;
|
||||
case SPECIALMODE_MODAL:
|
||||
if((iskbd && !polarity && kbdkey == SDLK_ESCAPE) || e.type == SDL_QUIT) {
|
||||
//Negative response.
|
||||
modal_dialog_confirm = false;
|
||||
modal_dialog_active = false;
|
||||
screenmod->clear_modal();
|
||||
special_mode = SPECIALMODE_NORMAL;
|
||||
//We NAK the command in case modal dialog was somehow entered with command active.
|
||||
cmdline.key(SPECIAL_NAK);
|
||||
ui_ungrab_keys(true);
|
||||
platform::set_modal_pause(false);
|
||||
modal_dialog_was_active = false;
|
||||
mutex::holder h(*ui_mutex);
|
||||
ui_condition->signal();
|
||||
}
|
||||
if(iskbd && !polarity && (kbdkey == SDLK_RETURN || kbdkey == SDLK_KP_ENTER)) {
|
||||
//Positive response.
|
||||
modal_dialog_active = false;
|
||||
screenmod->clear_modal();
|
||||
modal_dialog_was_active = false;
|
||||
special_mode = SPECIALMODE_NORMAL;
|
||||
//We NAK the command in case modal dialog was somehow entered with command active.
|
||||
cmdline.key(SPECIAL_NAK);
|
||||
platform::set_modal_pause(false);
|
||||
ui_ungrab_keys(true);
|
||||
mutex::holder h(*ui_mutex);
|
||||
ui_condition->signal();
|
||||
}
|
||||
break;
|
||||
}
|
||||
timer_triggered = false;
|
||||
if(e.type == SDL_QUIT)
|
||||
ui_handle_quit_signal();
|
||||
//Yes, normal key handling is done even if in commandline or other modal mode.
|
||||
keypress k;
|
||||
if(translate_sdl_key(e, k)) {
|
||||
platform::queue(k);
|
||||
}
|
||||
if(translate_sdl_joystick(e, k)) {
|
||||
platform::queue(k);
|
||||
}
|
||||
//Handle repaints.
|
||||
bool status = false;
|
||||
bool screen = false;
|
||||
bool pmessages = false;
|
||||
bool new_fsc = false;
|
||||
bool toggle_fsc = false;
|
||||
{
|
||||
mutex::holder h(*ui_mutex);
|
||||
pmessages = messages_dirty;
|
||||
status = status_dirty;
|
||||
screen = screen_dirty;
|
||||
new_fsc = fullscreen_console;
|
||||
messages_dirty = false;
|
||||
screen_dirty = false;
|
||||
status_dirty = false;
|
||||
if(new_fsc != fullscreen_console_active)
|
||||
toggle_fsc = true;
|
||||
else
|
||||
toggle_fsc = false;
|
||||
repaint_in_flight = false;
|
||||
}
|
||||
//If screen is dirty (irrespective if full repaint would be done), render the screeen.
|
||||
if(screen)
|
||||
render_framebuffer();
|
||||
bool any = status || pmessages || screen || commandline_updated || toggle_fsc || full;
|
||||
if(special_mode == SPECIALMODE_MODAL || full) {
|
||||
//FIXME: Use less intensive paint for SPECIALMODE_MODAL.
|
||||
screenmod->repaint_full();
|
||||
any = true;
|
||||
} else {
|
||||
if(status) {
|
||||
screenmod->repaint_status();
|
||||
}
|
||||
if(pmessages) {
|
||||
screenmod->repaint_messages();
|
||||
}
|
||||
if(screen) {
|
||||
screenmod->repaint_screen();
|
||||
}
|
||||
if(commandline_updated) {
|
||||
screenmod->repaint_commandline();
|
||||
}
|
||||
if(toggle_fsc) {
|
||||
screenmod->set_fullscreen_console(new_fsc);
|
||||
fullscreen_console_active = new_fsc;
|
||||
}
|
||||
}
|
||||
if(any)
|
||||
screenmod->flip();
|
||||
}
|
||||
}
|
||||
|
||||
void graphics_plugin::init() throw()
|
||||
{
|
||||
if(!screenmod)
|
||||
screenmod = new screen_model;
|
||||
if(!ui_mutex)
|
||||
ui_mutex = &mutex::aquire();
|
||||
if(!ui_condition)
|
||||
ui_condition = &condition::aquire(*ui_mutex);
|
||||
screenmod->set_command_line(&cmdline);
|
||||
arm_sigalrm();
|
||||
ui_thread = &thread_id::me();
|
||||
init_sdl_keys();
|
||||
if(!sdl_init) {
|
||||
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_TIMER);
|
||||
SDL_EnableUNICODE(true);
|
||||
sdl_init = true;
|
||||
timer_id = SDL_AddTimer(30, timer_cb, NULL);
|
||||
}
|
||||
//Doing full repaint will open the window.
|
||||
screenmod->repaint_full();
|
||||
std::string windowname = "lsnes rr" + lsnes_version + "[" + bsnes_core_version + "]";
|
||||
SDL_WM_SetCaption(windowname.c_str(), "lsnes");
|
||||
}
|
||||
|
||||
void graphics_plugin::quit() throw()
|
||||
{
|
||||
if(sdl_init) {
|
||||
SDL_Quit();
|
||||
sdl_init = false;
|
||||
SDL_RemoveTimer(timer_id);
|
||||
}
|
||||
deinit_sdl_keys();
|
||||
}
|
||||
|
||||
void graphics_plugin::notify_message() throw()
|
||||
{
|
||||
messages_dirty = true;
|
||||
wake_ui();
|
||||
}
|
||||
|
||||
void graphics_plugin::notify_status() throw()
|
||||
{
|
||||
status_dirty = true;
|
||||
wake_ui();
|
||||
}
|
||||
|
||||
void graphics_plugin::notify_screen() throw()
|
||||
{
|
||||
screen_dirty = true;
|
||||
wake_ui();
|
||||
}
|
||||
|
||||
bool graphics_plugin::modal_message(const std::string& text, bool confirm) throw()
|
||||
{
|
||||
bool answer = false;
|
||||
try {
|
||||
//Make the UI thread do the prompting.
|
||||
mutex::holder h(*ui_mutex);
|
||||
modal_dialog_active = true;
|
||||
modal_dialog_text = text;
|
||||
modal_dialog_confirm = confirm;
|
||||
while(modal_dialog_active)
|
||||
ui_condition->wait(100000);
|
||||
answer = modal_dialog_confirm;
|
||||
} catch(std::bad_alloc& e) {
|
||||
OOM_panic();
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
void graphics_plugin::fatal_error() throw()
|
||||
{
|
||||
//Fun... This can be called from any thread.
|
||||
if(ui_thread->is_me()) {
|
||||
ui_panic();
|
||||
} else {
|
||||
paniced = true;
|
||||
wake_ui();
|
||||
//Busywait as program state may be very unpredictable.
|
||||
while(!panic_ack);
|
||||
}
|
||||
}
|
||||
|
||||
const char* graphics_plugin::name = "SDL graphics plugin";
|
|
@ -1,126 +0,0 @@
|
|||
#include "core/window.hpp"
|
||||
#include "platform/sdl/platform.hpp"
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
std::map<unsigned, keyboard_key*> joyaxis;
|
||||
std::map<unsigned, keyboard_key*> joybutton;
|
||||
std::map<unsigned, keyboard_key*> joyhat;
|
||||
std::set<SDL_Joystick*> joysticksx;
|
||||
}
|
||||
|
||||
unsigned translate_sdl_joystick(SDL_Event& e, keypress& k)
|
||||
{
|
||||
if(e.type == SDL_JOYAXISMOTION) {
|
||||
unsigned num = static_cast<unsigned>(e.jaxis.which) * 256 +
|
||||
static_cast<unsigned>(e.jaxis.axis);
|
||||
if(joyaxis.count(num)) {
|
||||
k = keypress(keyboard_modifier_set(), *joyaxis[num], e.jaxis.value);
|
||||
return 1;
|
||||
}
|
||||
} else if(e.type == SDL_JOYHATMOTION) {
|
||||
unsigned num = static_cast<unsigned>(e.jhat.which) * 256 +
|
||||
static_cast<unsigned>(e.jhat.hat);
|
||||
short v = 0;
|
||||
if(e.jhat.value & SDL_HAT_UP)
|
||||
v |= 1;
|
||||
if(e.jhat.value & SDL_HAT_RIGHT)
|
||||
v |= 2;
|
||||
if(e.jhat.value & SDL_HAT_DOWN)
|
||||
v |= 4;
|
||||
if(e.jhat.value & SDL_HAT_LEFT)
|
||||
v |= 8;
|
||||
if(joyhat.count(num)) {
|
||||
k = keypress(keyboard_modifier_set(), *joyhat[num], v);
|
||||
return 1;
|
||||
}
|
||||
} else if(e.type == SDL_JOYBUTTONDOWN || e.type == SDL_JOYBUTTONUP) {
|
||||
unsigned num = static_cast<unsigned>(e.jbutton.which) * 256 +
|
||||
static_cast<unsigned>(e.jbutton.button);
|
||||
if(joybutton.count(num)) {
|
||||
k = keypress(keyboard_modifier_set(), *joybutton[num], (e.type == SDL_JOYBUTTONDOWN) ? 1 : 0);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
void joystick_plugin::init() throw()
|
||||
{
|
||||
int joysticks = SDL_NumJoysticks();
|
||||
if(!joysticks) {
|
||||
messages << "No joysticks detected." << std::endl;
|
||||
} else {
|
||||
messages << joysticks << " joystick(s) detected." << std::endl;
|
||||
for(int i = 0; i < joysticks; i++) {
|
||||
SDL_Joystick* j = SDL_JoystickOpen(i);
|
||||
if(!j) {
|
||||
messages << "Joystick #" << i << ": Can't open!" << std::endl;
|
||||
continue;
|
||||
}
|
||||
joysticksx.insert(j);
|
||||
messages << "Joystick #" << i << ": " << SDL_JoystickName(i) << "("
|
||||
<< SDL_JoystickNumAxes(j) << " axes, " << SDL_JoystickNumButtons(j)
|
||||
<< " buttons, " << SDL_JoystickNumHats(j) << " hats)." << std::endl;
|
||||
for(int k = 0; k < SDL_JoystickNumAxes(j); k++) {
|
||||
unsigned num = 256 * i + k;
|
||||
std::ostringstream x;
|
||||
x << "joystick" << i << "axis" << k;
|
||||
keyboard_axis_calibration cal;
|
||||
cal.mode = 1;
|
||||
cal.esign_a = -1;
|
||||
cal.esign_b = 1;
|
||||
cal.left = -32767;
|
||||
cal.center = 0;
|
||||
cal.right = 32767;
|
||||
cal.nullwidth = 0.3;
|
||||
joyaxis[num] = new keyboard_key_axis(lsnes_kbd, x.str(), "joystick", cal);
|
||||
}
|
||||
for(int k = 0; k < SDL_JoystickNumButtons(j); k++) {
|
||||
unsigned num = 256 * i + k;
|
||||
std::ostringstream x;
|
||||
x << "joystick" << i << "button" << k;
|
||||
joybutton[num] = new keyboard_key_key(lsnes_kbd, x.str(), "joystick");
|
||||
}
|
||||
for(int k = 0; k < SDL_JoystickNumHats(j); k++) {
|
||||
unsigned num = 256 * i + k;
|
||||
std::ostringstream x;
|
||||
x << "joystick" << i << "hat" << k;
|
||||
joyhat[num] = new keyboard_key_hat(lsnes_kbd, x.str(), "joystick");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void joystick_plugin::quit() throw()
|
||||
{
|
||||
for(auto i : joyaxis)
|
||||
delete i.second;
|
||||
for(auto i : joyhat)
|
||||
delete i.second;
|
||||
for(auto i : joybutton)
|
||||
delete i.second;
|
||||
for(auto i : joysticksx)
|
||||
SDL_JoystickClose(i);
|
||||
joyaxis.clear();
|
||||
joyhat.clear();
|
||||
joybutton.clear();
|
||||
joysticksx.clear();
|
||||
}
|
||||
|
||||
void joystick_plugin::thread_fn() throw()
|
||||
{
|
||||
//Exit instantly.
|
||||
}
|
||||
|
||||
void joystick_plugin::signal() throw()
|
||||
{
|
||||
//Nothing to do.
|
||||
}
|
||||
|
||||
const char* joystick_plugin::name = "SDL joystick plugin";
|
|
@ -1,431 +0,0 @@
|
|||
#include "core/window.hpp"
|
||||
#include "platform/sdl/platform.hpp"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
struct sdl_modifier
|
||||
{
|
||||
const char* name;
|
||||
const char* linkname;
|
||||
unsigned sdlvalue;
|
||||
} modifiers_table[] = {
|
||||
{ "ctrl", NULL, 0 },
|
||||
{ "lctrl", "ctrl", KMOD_LCTRL },
|
||||
{ "rctrl", "ctrl", KMOD_RCTRL },
|
||||
{ "alt", NULL, 0 },
|
||||
{ "lalt", "alt", KMOD_LALT },
|
||||
{ "ralt", "alt", KMOD_RALT },
|
||||
{ "shift", NULL, 0 },
|
||||
{ "lshift", "shift", KMOD_LSHIFT },
|
||||
{ "rshift", "shift", KMOD_RSHIFT },
|
||||
{ "meta", NULL, 0 },
|
||||
{ "lmeta", "meta", KMOD_LMETA },
|
||||
{ "rmeta", "meta", KMOD_RMETA },
|
||||
{ "num", NULL, KMOD_NUM },
|
||||
{ "caps", NULL, KMOD_CAPS },
|
||||
{ "mode", NULL, KMOD_MODE },
|
||||
{ NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
struct sdl_key
|
||||
{
|
||||
const char* name;
|
||||
const char* clazz;
|
||||
unsigned symbol;
|
||||
} keys_table[] = {
|
||||
{"backspace", "editing", SDLK_BACKSPACE },
|
||||
{"tab", "editing", SDLK_TAB },
|
||||
{"clear", "editing", SDLK_CLEAR },
|
||||
{"return", "editing", SDLK_RETURN },
|
||||
{"pause", "special", SDLK_PAUSE },
|
||||
{"escape", "editing", SDLK_ESCAPE },
|
||||
{"space", "characters", SDLK_SPACE },
|
||||
{"exclaim", "characters", SDLK_EXCLAIM },
|
||||
{"quotedbl", "characters", SDLK_QUOTEDBL },
|
||||
{"hash", "characters", SDLK_HASH },
|
||||
{"dollar", "characters", SDLK_DOLLAR },
|
||||
{"ampersand", "characters", SDLK_AMPERSAND },
|
||||
{"quote", "characters", SDLK_QUOTE },
|
||||
{"leftparen", "characters", SDLK_LEFTPAREN },
|
||||
{"rightparen", "characters", SDLK_RIGHTPAREN },
|
||||
{"asterisk", "characters", SDLK_ASTERISK },
|
||||
{"plus", "characters", SDLK_PLUS },
|
||||
{"comma", "characters", SDLK_COMMA },
|
||||
{"minus", "characters", SDLK_MINUS },
|
||||
{"period", "characters", SDLK_PERIOD },
|
||||
{"slash", "characters", SDLK_SLASH },
|
||||
{"0", "numeric", SDLK_0 },
|
||||
{"1", "numeric", SDLK_1 },
|
||||
{"2", "numeric", SDLK_2 },
|
||||
{"3", "numeric", SDLK_3 },
|
||||
{"4", "numeric", SDLK_4 },
|
||||
{"5", "numeric", SDLK_5 },
|
||||
{"6", "numeric", SDLK_6 },
|
||||
{"7", "numeric", SDLK_7 },
|
||||
{"8", "numeric", SDLK_8 },
|
||||
{"9", "numeric", SDLK_9 },
|
||||
{"colon", "characters", SDLK_COLON },
|
||||
{"semicolon", "characters", SDLK_SEMICOLON },
|
||||
{"less", "characters", SDLK_LESS },
|
||||
{"equals", "characters", SDLK_EQUALS },
|
||||
{"greater", "characters", SDLK_GREATER },
|
||||
{"question", "characters", SDLK_QUESTION },
|
||||
{"at", "characters", SDLK_AT },
|
||||
{"leftbracket", "characters", SDLK_LEFTBRACKET },
|
||||
{"backslash", "characters", SDLK_BACKSLASH },
|
||||
{"rightbracket", "characters", SDLK_RIGHTBRACKET },
|
||||
{"caret", "characters", SDLK_CARET },
|
||||
{"underscore", "characters", SDLK_UNDERSCORE },
|
||||
{"backquote", "characters", SDLK_BACKQUOTE },
|
||||
{"a", "alphabetic", SDLK_a },
|
||||
{"b", "alphabetic", SDLK_b },
|
||||
{"c", "alphabetic", SDLK_c },
|
||||
{"d", "alphabetic", SDLK_d },
|
||||
{"e", "alphabetic", SDLK_e },
|
||||
{"f", "alphabetic", SDLK_f },
|
||||
{"g", "alphabetic", SDLK_g },
|
||||
{"h", "alphabetic", SDLK_h },
|
||||
{"i", "alphabetic", SDLK_i },
|
||||
{"j", "alphabetic", SDLK_j },
|
||||
{"k", "alphabetic", SDLK_k },
|
||||
{"l", "alphabetic", SDLK_l },
|
||||
{"m", "alphabetic", SDLK_m },
|
||||
{"n", "alphabetic", SDLK_n },
|
||||
{"o", "alphabetic", SDLK_o },
|
||||
{"p", "alphabetic", SDLK_p },
|
||||
{"q", "alphabetic", SDLK_q },
|
||||
{"r", "alphabetic", SDLK_r },
|
||||
{"s", "alphabetic", SDLK_s },
|
||||
{"t", "alphabetic", SDLK_t },
|
||||
{"u", "alphabetic", SDLK_u },
|
||||
{"v", "alphabetic", SDLK_v },
|
||||
{"w", "alphabetic", SDLK_w },
|
||||
{"x", "alphabetic", SDLK_x },
|
||||
{"y", "alphabetic", SDLK_y },
|
||||
{"z", "alphabetic", SDLK_z },
|
||||
{"delete", "editing", SDLK_DELETE },
|
||||
{"world_0", "international",SDLK_WORLD_0 },
|
||||
{"world_1", "international",SDLK_WORLD_1 },
|
||||
{"world_2", "international",SDLK_WORLD_2 },
|
||||
{"world_3", "international",SDLK_WORLD_3 },
|
||||
{"world_4", "international",SDLK_WORLD_4 },
|
||||
{"world_5", "international",SDLK_WORLD_5 },
|
||||
{"world_6", "international",SDLK_WORLD_6 },
|
||||
{"world_7", "international",SDLK_WORLD_7 },
|
||||
{"world_8", "international",SDLK_WORLD_8 },
|
||||
{"world_9", "international",SDLK_WORLD_9 },
|
||||
{"world_10", "international",SDLK_WORLD_10 },
|
||||
{"world_11", "international",SDLK_WORLD_11 },
|
||||
{"world_12", "international",SDLK_WORLD_12 },
|
||||
{"world_13", "international",SDLK_WORLD_13 },
|
||||
{"world_14", "international",SDLK_WORLD_14 },
|
||||
{"world_15", "international",SDLK_WORLD_15 },
|
||||
{"world_16", "international",SDLK_WORLD_16 },
|
||||
{"world_17", "international",SDLK_WORLD_17 },
|
||||
{"world_18", "international",SDLK_WORLD_18 },
|
||||
{"world_19", "international",SDLK_WORLD_19 },
|
||||
{"world_20", "international",SDLK_WORLD_20 },
|
||||
{"world_21", "international",SDLK_WORLD_21 },
|
||||
{"world_22", "international",SDLK_WORLD_22 },
|
||||
{"world_23", "international",SDLK_WORLD_23 },
|
||||
{"world_24", "international",SDLK_WORLD_24 },
|
||||
{"world_25", "international",SDLK_WORLD_25 },
|
||||
{"world_26", "international",SDLK_WORLD_26 },
|
||||
{"world_27", "international",SDLK_WORLD_27 },
|
||||
{"world_28", "international",SDLK_WORLD_28 },
|
||||
{"world_29", "international",SDLK_WORLD_29 },
|
||||
{"world_30", "international",SDLK_WORLD_30 },
|
||||
{"world_31", "international",SDLK_WORLD_31 },
|
||||
{"world_32", "international",SDLK_WORLD_32 },
|
||||
{"world_33", "international",SDLK_WORLD_33 },
|
||||
{"world_34", "international",SDLK_WORLD_34 },
|
||||
{"world_35", "international",SDLK_WORLD_35 },
|
||||
{"world_36", "international",SDLK_WORLD_36 },
|
||||
{"world_37", "international",SDLK_WORLD_37 },
|
||||
{"world_38", "international",SDLK_WORLD_38 },
|
||||
{"world_39", "international",SDLK_WORLD_39 },
|
||||
{"world_40", "international",SDLK_WORLD_40 },
|
||||
{"world_41", "international",SDLK_WORLD_41 },
|
||||
{"world_42", "international",SDLK_WORLD_42 },
|
||||
{"world_43", "international",SDLK_WORLD_43 },
|
||||
{"world_44", "international",SDLK_WORLD_44 },
|
||||
{"world_45", "international",SDLK_WORLD_45 },
|
||||
{"world_46", "international",SDLK_WORLD_46 },
|
||||
{"world_47", "international",SDLK_WORLD_47 },
|
||||
{"world_48", "international",SDLK_WORLD_48 },
|
||||
{"world_49", "international",SDLK_WORLD_49 },
|
||||
{"world_50", "international",SDLK_WORLD_50 },
|
||||
{"world_51", "international",SDLK_WORLD_51 },
|
||||
{"world_52", "international",SDLK_WORLD_52 },
|
||||
{"world_53", "international",SDLK_WORLD_53 },
|
||||
{"world_54", "international",SDLK_WORLD_54 },
|
||||
{"world_55", "international",SDLK_WORLD_55 },
|
||||
{"world_56", "international",SDLK_WORLD_56 },
|
||||
{"world_57", "international",SDLK_WORLD_57 },
|
||||
{"world_58", "international",SDLK_WORLD_58 },
|
||||
{"world_59", "international",SDLK_WORLD_59 },
|
||||
{"world_60", "international",SDLK_WORLD_60 },
|
||||
{"world_61", "international",SDLK_WORLD_61 },
|
||||
{"world_62", "international",SDLK_WORLD_62 },
|
||||
{"world_63", "international",SDLK_WORLD_63 },
|
||||
{"world_64", "international",SDLK_WORLD_64 },
|
||||
{"world_65", "international",SDLK_WORLD_65 },
|
||||
{"world_66", "international",SDLK_WORLD_66 },
|
||||
{"world_67", "international",SDLK_WORLD_67 },
|
||||
{"world_68", "international",SDLK_WORLD_68 },
|
||||
{"world_69", "international",SDLK_WORLD_69 },
|
||||
{"world_70", "international",SDLK_WORLD_70 },
|
||||
{"world_71", "international",SDLK_WORLD_71 },
|
||||
{"world_72", "international",SDLK_WORLD_72 },
|
||||
{"world_73", "international",SDLK_WORLD_73 },
|
||||
{"world_74", "international",SDLK_WORLD_74 },
|
||||
{"world_75", "international",SDLK_WORLD_75 },
|
||||
{"world_76", "international",SDLK_WORLD_76 },
|
||||
{"world_77", "international",SDLK_WORLD_77 },
|
||||
{"world_78", "international",SDLK_WORLD_78 },
|
||||
{"world_79", "international",SDLK_WORLD_79 },
|
||||
{"world_80", "international",SDLK_WORLD_80 },
|
||||
{"world_81", "international",SDLK_WORLD_81 },
|
||||
{"world_82", "international",SDLK_WORLD_82 },
|
||||
{"world_83", "international",SDLK_WORLD_83 },
|
||||
{"world_84", "international",SDLK_WORLD_84 },
|
||||
{"world_85", "international",SDLK_WORLD_85 },
|
||||
{"world_86", "international",SDLK_WORLD_86 },
|
||||
{"world_87", "international",SDLK_WORLD_87 },
|
||||
{"world_88", "international",SDLK_WORLD_88 },
|
||||
{"world_89", "international",SDLK_WORLD_89 },
|
||||
{"world_90", "international",SDLK_WORLD_90 },
|
||||
{"world_91", "international",SDLK_WORLD_91 },
|
||||
{"world_92", "international",SDLK_WORLD_92 },
|
||||
{"world_93", "international",SDLK_WORLD_93 },
|
||||
{"world_94", "international",SDLK_WORLD_94 },
|
||||
{"world_95", "international",SDLK_WORLD_95 },
|
||||
{"kp0", "numeric", SDLK_KP0 },
|
||||
{"kp1", "numeric", SDLK_KP1 },
|
||||
{"kp2", "numeric", SDLK_KP2 },
|
||||
{"kp3", "numeric", SDLK_KP3 },
|
||||
{"kp4", "numeric", SDLK_KP4 },
|
||||
{"kp5", "numeric", SDLK_KP5 },
|
||||
{"kp6", "numeric", SDLK_KP6 },
|
||||
{"kp7", "numeric", SDLK_KP7 },
|
||||
{"kp8", "numeric", SDLK_KP8 },
|
||||
{"kp9", "numeric", SDLK_KP9 },
|
||||
{"kp_period", "characters", SDLK_KP_PERIOD },
|
||||
{"kp_divide", "characters", SDLK_KP_DIVIDE },
|
||||
{"kp_multiply", "characters", SDLK_KP_MULTIPLY },
|
||||
{"kp_minus", "characters", SDLK_KP_MINUS },
|
||||
{"kp_plus", "characters", SDLK_KP_PLUS },
|
||||
{"kp_enter", "characters", SDLK_KP_ENTER },
|
||||
{"kp_equals", "characters", SDLK_KP_EQUALS },
|
||||
{"up", "editing", SDLK_UP },
|
||||
{"down", "editing", SDLK_DOWN },
|
||||
{"right", "editing", SDLK_RIGHT },
|
||||
{"left", "editing", SDLK_LEFT },
|
||||
{"insert", "editing", SDLK_INSERT },
|
||||
{"home", "editing", SDLK_HOME },
|
||||
{"end", "editing", SDLK_END },
|
||||
{"pageup", "editing", SDLK_PAGEUP },
|
||||
{"pagedown", "editing", SDLK_PAGEDOWN },
|
||||
{"f1", "F-keys", SDLK_F1 },
|
||||
{"f2", "F-keys", SDLK_F2 },
|
||||
{"f3", "F-keys", SDLK_F3 },
|
||||
{"f4", "F-keys", SDLK_F4 },
|
||||
{"f5", "F-keys", SDLK_F5 },
|
||||
{"f6", "F-keys", SDLK_F6 },
|
||||
{"f7", "F-keys", SDLK_F7 },
|
||||
{"f8", "F-keys", SDLK_F8 },
|
||||
{"f9", "F-keys", SDLK_F9 },
|
||||
{"f10", "F-keys", SDLK_F10 },
|
||||
{"f11", "F-keys", SDLK_F11 },
|
||||
{"f12", "F-keys", SDLK_F12 },
|
||||
{"f13", "F-keys", SDLK_F13 },
|
||||
{"f14", "F-keys", SDLK_F14 },
|
||||
{"f15", "F-keys", SDLK_F15 },
|
||||
{"numlock", "locks", SDLK_NUMLOCK },
|
||||
{"capslock", "locks", SDLK_CAPSLOCK },
|
||||
{"scrollock", "locks", SDLK_SCROLLOCK },
|
||||
{"rshift", "modifiers", SDLK_RSHIFT },
|
||||
{"lshift", "modifiers", SDLK_LSHIFT },
|
||||
{"rctrl", "modifiers", SDLK_RCTRL },
|
||||
{"lctrl", "modifiers", SDLK_LCTRL },
|
||||
{"ralt", "modifiers", SDLK_RALT },
|
||||
{"lalt", "modifiers", SDLK_LALT },
|
||||
{"rmeta", "modifiers", SDLK_RMETA },
|
||||
{"lmeta", "modifiers", SDLK_LMETA },
|
||||
{"lsuper", "modifiers", SDLK_LSUPER },
|
||||
{"rsuper", "modifiers", SDLK_RSUPER },
|
||||
{"mode", "modifiers", SDLK_MODE },
|
||||
{"compose", "modifiers", SDLK_COMPOSE },
|
||||
{"help", "special", SDLK_HELP },
|
||||
{"print", "special", SDLK_PRINT },
|
||||
{"sysreq", "special", SDLK_SYSREQ },
|
||||
{"break", "special", SDLK_BREAK },
|
||||
{"menu", "special", SDLK_MENU },
|
||||
{"power", "special", SDLK_POWER },
|
||||
{"euro", "characters", SDLK_EURO },
|
||||
{"undo", "special", SDLK_UNDO },
|
||||
{NULL, 0 }
|
||||
};
|
||||
|
||||
std::map<unsigned, keyboard_modifier*> supported_modifiers;
|
||||
std::map<unsigned, keyboard_key_key*> scancodekeys;
|
||||
std::map<unsigned, keyboard_key_key*> symbolkeys;
|
||||
}
|
||||
|
||||
unsigned translate_sdl_key(SDL_Event& e, keypress& k)
|
||||
{
|
||||
if(e.type == SDL_KEYDOWN || e.type == SDL_KEYUP) {
|
||||
keyboard_modifier_set modifiers;
|
||||
short value = (e.type == SDL_KEYDOWN) ? 1 : 0;
|
||||
SDL_KeyboardEvent& ke = e.key;
|
||||
SDL_keysym sym = ke.keysym;
|
||||
uint8_t scancode = sym.scancode;
|
||||
unsigned symbol = sym.sym;
|
||||
for(auto l : supported_modifiers)
|
||||
if(sym.mod & l.first)
|
||||
modifiers.add(*l.second);
|
||||
if(symbolkeys.count(symbol))
|
||||
k = keypress(modifiers, *scancodekeys[scancode], *symbolkeys[symbol], value);
|
||||
else
|
||||
k = keypress(modifiers, *scancodekeys[scancode], value);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
uint32_t get_command_edit_operation(SDL_Event& e, bool enable)
|
||||
{
|
||||
if(!enable)
|
||||
return SPECIAL_NOOP;
|
||||
uint32_t press = (e.type == SDL_KEYDOWN) ? PRESSED_MASK : 0;
|
||||
//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;
|
||||
//Return/Enter is special.
|
||||
if(e.key.keysym.sym == SDLK_RETURN || e.key.keysym.sym == SDLK_KP_ENTER)
|
||||
return (e.type == SDL_KEYUP) ? SPECIAL_ACK : SPECIAL_NOOP;
|
||||
//Other special keys.
|
||||
switch(e.key.keysym.sym) {
|
||||
case SDLK_BACKSPACE:
|
||||
return SPECIAL_BACKSPACE | press;
|
||||
case SDLK_INSERT:
|
||||
case SDLK_KP0:
|
||||
return SPECIAL_INSERT | press;
|
||||
case SDLK_DELETE:
|
||||
case SDLK_KP_PERIOD:
|
||||
return SPECIAL_DELETE | press;
|
||||
case SDLK_HOME:
|
||||
case SDLK_KP7:
|
||||
return SPECIAL_HOME | press;
|
||||
case SDLK_END:
|
||||
case SDLK_KP1:
|
||||
return SPECIAL_END | press;
|
||||
case SDLK_PAGEUP:
|
||||
case SDLK_KP9:
|
||||
return SPECIAL_PGUP | press;
|
||||
case SDLK_PAGEDOWN:
|
||||
case SDLK_KP3:
|
||||
return SPECIAL_PGDN | press;
|
||||
case SDLK_UP:
|
||||
case SDLK_KP8:
|
||||
return SPECIAL_UP | press;
|
||||
case SDLK_DOWN:
|
||||
case SDLK_KP2:
|
||||
return SPECIAL_DOWN | press;
|
||||
case SDLK_LEFT:
|
||||
case SDLK_KP4:
|
||||
return SPECIAL_LEFT | press;
|
||||
case SDLK_RIGHT:
|
||||
case SDLK_KP6:
|
||||
return SPECIAL_RIGHT | press;
|
||||
case SDLK_KP5:
|
||||
return SPECIAL_NOOP;
|
||||
};
|
||||
return e.key.keysym.unicode | press;
|
||||
}
|
||||
|
||||
void init_sdl_keys()
|
||||
{
|
||||
struct sdl_modifier* m = modifiers_table;
|
||||
while(m->name) {
|
||||
keyboard_modifier* m2;
|
||||
if(m->linkname)
|
||||
m2 = new keyboard_modifier(lsnes_kbd, m->name, m->linkname);
|
||||
else
|
||||
m2 = new keyboard_modifier(lsnes_kbd, m->name);
|
||||
if(m->sdlvalue)
|
||||
supported_modifiers[m->sdlvalue] = m2;
|
||||
m++;
|
||||
}
|
||||
struct sdl_key* k = keys_table;
|
||||
while(k->name) {
|
||||
symbolkeys[k->symbol] = new keyboard_key_key(lsnes_kbd, k->name, k->clazz);
|
||||
k++;
|
||||
}
|
||||
for(unsigned i = 0; i < 256; i++) {
|
||||
std::ostringstream x;
|
||||
x << "key" << i;
|
||||
scancodekeys[i] = new keyboard_key_key(lsnes_kbd, x.str(), "scancode");
|
||||
}
|
||||
}
|
||||
|
||||
void deinit_sdl_keys()
|
||||
{
|
||||
for(auto i : supported_modifiers)
|
||||
delete i.second;
|
||||
for(auto i : scancodekeys)
|
||||
delete i.second;
|
||||
for(auto i : symbolkeys)
|
||||
delete i.second;
|
||||
supported_modifiers.clear();
|
||||
scancodekeys.clear();
|
||||
symbolkeys.clear();
|
||||
}
|
|
@ -1,237 +0,0 @@
|
|||
#include "lsnes.hpp"
|
||||
#include "core/emucore.hpp"
|
||||
|
||||
#include "core/audioapi.hpp"
|
||||
#include "core/command.hpp"
|
||||
#include "core/framerate.hpp"
|
||||
#include "core/keymapper.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/string.hpp"
|
||||
#include "library/minmax.hpp"
|
||||
#include "library/string.hpp"
|
||||
#include "library/zip.hpp"
|
||||
|
||||
#include "platform/sdl/platform.hpp"
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sstream>
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE) || defined(__APPLE__)
|
||||
#include "SDL_main.h"
|
||||
#endif
|
||||
|
||||
bool dummy_interface = false;
|
||||
|
||||
struct moviefile generate_movie_template(std::vector<std::string> cmdline, loaded_rom& r)
|
||||
{
|
||||
struct moviefile movie;
|
||||
std::vector<class port_type*> ports;
|
||||
for(unsigned i = 0; i <= core_userports; i++)
|
||||
ports.push_back(&core_portgroup.get_default_type(i));
|
||||
movie.coreversion = bsnes_core_version;
|
||||
movie.projectid = get_random_hexstring(40);
|
||||
movie.gametype = &r.rtype->combine_region(*r.region);
|
||||
for(size_t i = 0; i < sizeof(r.romimg)/sizeof(r.romimg[0]); i++) {
|
||||
movie.romimg_sha256[i] = r.romimg[i].sha_256;
|
||||
movie.romxml_sha256[i] = r.romxml[i].sha_256;
|
||||
}
|
||||
movie.movie_sram = load_sram_commandline(cmdline);
|
||||
for(auto i = cmdline.begin(); i != cmdline.end(); i++) {
|
||||
std::string o = *i;
|
||||
if(o.length() >= 9 && o.substr(0, 9) == "--prefix=")
|
||||
movie.prefix = sanitize_prefix(o.substr(9));
|
||||
for(unsigned i = 1; i <= core_userports; i++) {
|
||||
std::string optn = (stringfmt() << "--port" << i << "=").str();
|
||||
size_t optnl = optn.length();
|
||||
if(o.length() >= optnl && o.substr(0, optnl) == optn)
|
||||
ports[i] = &core_portgroup.get_type(o.substr(optnl));
|
||||
}
|
||||
if(o.length() >= 11 && o.substr(0, 11) == "--gamename=")
|
||||
movie.gamename = o.substr(11);
|
||||
if(o.length() >= 9 && o.substr(0, 9) == "--author=") {
|
||||
std::string line = o.substr(9);
|
||||
auto g = split_author(line);
|
||||
movie.authors.push_back(g);
|
||||
}
|
||||
if(o.length() >= 13 && o.substr(0, 13) == "--rtc-second=") {
|
||||
movie.rtc_second = movie.movie_rtc_second = parse_value<int64_t>(o.substr(13));
|
||||
}
|
||||
if(o.length() >= 16 && o.substr(0, 16) == "--rtc-subsecond=") {
|
||||
movie.rtc_subsecond = movie.movie_rtc_subsecond = parse_value<int64_t>(o.substr(16));
|
||||
}
|
||||
if(o.length() >= 19 && o.substr(0, 19) == "--anchor-savestate=") {
|
||||
movie.anchor_savestate = read_file_relative(o.substr(19), "");
|
||||
}
|
||||
}
|
||||
movie.ports = &port_type_set::make(ports);
|
||||
movie.input.clear(*movie.ports);
|
||||
|
||||
return movie;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void run_extra_scripts(const std::vector<std::string>& cmdline)
|
||||
{
|
||||
for(auto i = cmdline.begin(); i != cmdline.end(); i++) {
|
||||
std::string o = *i;
|
||||
if(o.length() >= 6 && o.substr(0, 6) == "--run=") {
|
||||
std::string file = o.substr(6);
|
||||
messages << "--- Running " << file << " --- " << std::endl;
|
||||
lsnes_cmd.invoke("run-script " + file);
|
||||
messages << "--- End running " << file << " --- " << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sdl_main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_to_succeed = false)
|
||||
throw(std::bad_alloc, std::runtime_error);
|
||||
|
||||
struct emu_args
|
||||
{
|
||||
struct loaded_rom* rom;
|
||||
struct moviefile* initial;
|
||||
bool load_has_to_succeed;
|
||||
};
|
||||
|
||||
void* emulator_thread(void* _args)
|
||||
{
|
||||
struct emu_args* args = reinterpret_cast<struct emu_args*>(_args);
|
||||
try {
|
||||
main_loop(*args->rom, *args->initial, args->load_has_to_succeed);
|
||||
notify_emulator_exit();
|
||||
} catch(std::bad_alloc& e) {
|
||||
OOM_panic();
|
||||
} catch(std::exception& e) {
|
||||
messages << "FATAL: " << e.what() << std::endl;
|
||||
platform::fatal_error();
|
||||
}
|
||||
}
|
||||
|
||||
void* joystick_thread(void* _args)
|
||||
{
|
||||
joystick_plugin::thread_fn();
|
||||
}
|
||||
|
||||
void sdl_main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_to_succeed)
|
||||
throw(std::bad_alloc, std::runtime_error)
|
||||
{
|
||||
try {
|
||||
struct emu_args args;
|
||||
args.rom = &rom;
|
||||
args.initial = &initial;
|
||||
args.load_has_to_succeed = load_has_to_succeed;
|
||||
thread* t;
|
||||
thread* t2;
|
||||
t = &thread::create(emulator_thread, &args);
|
||||
t2 = &thread::create(joystick_thread, &args);
|
||||
ui_loop();
|
||||
joystick_plugin::signal();
|
||||
t2->join();
|
||||
t->join();
|
||||
delete t;
|
||||
delete t2;
|
||||
} catch(std::bad_alloc& e) {
|
||||
OOM_panic();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
reached_main();
|
||||
std::vector<std::string> cmdline;
|
||||
for(int i = 1; i < argc; i++)
|
||||
cmdline.push_back(argv[i]);
|
||||
if(cmdline.size() == 1 && cmdline[0] == "--version") {
|
||||
std::cout << "lsnes rr" << lsnes_version << " (" << lsnes_git_revision << ")" << std::endl;
|
||||
std::cout << get_core_identifier() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
do_basic_core_init();
|
||||
|
||||
set_random_seed();
|
||||
bsnes_core_version = get_core_identifier();
|
||||
platform::init();
|
||||
init_lua();
|
||||
|
||||
messages << "BSNES version: " << bsnes_core_version << std::endl;
|
||||
messages << "lsnes version: lsnes rr" << lsnes_version << std::endl;
|
||||
messages << "Command line is: ";
|
||||
for(auto k = cmdline.begin(); k != cmdline.end(); k++)
|
||||
messages << "\"" << *k << "\" ";
|
||||
messages << std::endl;
|
||||
|
||||
std::string cfgpath = get_config_path();
|
||||
create_lsnesrc();
|
||||
messages << "Saving per-user data to: " << get_config_path() << std::endl;
|
||||
messages << "--- Running lsnesrc --- " << std::endl;
|
||||
lsnes_set.set_storage_mode(true);
|
||||
lsnes_cmd.invoke("run-script " + cfgpath + "/lsnes.rc");
|
||||
lsnes_set.set_storage_mode(false);
|
||||
messages << "--- End running lsnesrc --- " << std::endl;
|
||||
|
||||
run_extra_scripts(cmdline);
|
||||
|
||||
messages << "--- Loading ROM ---" << std::endl;
|
||||
struct loaded_rom r;
|
||||
try {
|
||||
r = load_rom_from_commandline(cmdline);
|
||||
r.load(1000000000, 0);
|
||||
} catch(std::bad_alloc& e) {
|
||||
OOM_panic();
|
||||
} catch(std::exception& e) {
|
||||
messages << "FATAL: Can't load ROM: " << e.what() << std::endl;
|
||||
fatal_error();
|
||||
exit(1);
|
||||
}
|
||||
messages << "Detected region: " << r.rtype->combine_region(*r.region).get_name() << std::endl;
|
||||
set_nominal_framerate(r.region->approx_framerate());
|
||||
|
||||
messages << "--- Internal memory mappings ---" << std::endl;
|
||||
dump_region_map();
|
||||
messages << "--- End of Startup --- " << std::endl;
|
||||
moviefile movie;
|
||||
movie.force_corrupt = true;
|
||||
try {
|
||||
bool loaded = false;
|
||||
bool tried = false;
|
||||
for(auto i = cmdline.begin(); i != cmdline.end(); i++)
|
||||
if(i->length() > 0 && (*i)[0] != '-') {
|
||||
try {
|
||||
tried = true;
|
||||
movie = moviefile(*i);
|
||||
loaded = true;
|
||||
} catch(std::bad_alloc& e) {
|
||||
OOM_panic();
|
||||
} catch(std::exception& e) {
|
||||
messages << "Error loading '" << *i << "': " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
if(!tried)
|
||||
movie = generate_movie_template(cmdline, r);
|
||||
for(auto i = cmdline.begin(); i != cmdline.end(); i++)
|
||||
if(*i == "--pause")
|
||||
movie.start_paused = true;
|
||||
sdl_main_loop(r, movie);
|
||||
} catch(std::bad_alloc& e) {
|
||||
OOM_panic();
|
||||
} catch(std::exception& e) {
|
||||
messages << "FATAL: " << e.what() << std::endl;
|
||||
fatal_error();
|
||||
return 1;
|
||||
}
|
||||
lsnes_sdl_save_config();
|
||||
rrdata::close();
|
||||
quit_lua();
|
||||
platform::quit();
|
||||
quit_lua();
|
||||
return 0;
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
#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 axes.
|
||||
for(auto i : lsnes_kbd.all_keys()) {
|
||||
keyboard_key_axis* j = i->cast_axis();
|
||||
if(!j)
|
||||
continue;
|
||||
auto p = j->get_calibration();
|
||||
cfgfile << "set-axis " << i->get_name() << " " << calibration_to_mode(p);
|
||||
cfgfile << " minus=" << p.left << " zero=" << p.center << " plus=" << p.right
|
||||
<< " tolerance=" << p.nullwidth << std::endl;
|
||||
}
|
||||
//Settings.
|
||||
for(auto i : lsnes_set.get_settings_set()) {
|
||||
if(!lsnes_set.is_set(i))
|
||||
cfgfile << "unset-setting " << i << std::endl;
|
||||
else
|
||||
cfgfile << "set-setting " << i << " " << lsnes_set.get(i) << std::endl;
|
||||
}
|
||||
for(auto i : lsnes_set.get_invalid_values())
|
||||
cfgfile << "set-setting " << i.first << " " << i.second << std::endl;
|
||||
//Aliases.
|
||||
for(auto i : lsnes_cmd.get_aliases()) {
|
||||
std::string old_alias_value = lsnes_cmd.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());
|
||||
|
||||
}
|
|
@ -1,177 +0,0 @@
|
|||
#include "lsnes.hpp"
|
||||
|
||||
#include "core/audioapi.hpp"
|
||||
#include "core/command.hpp"
|
||||
#include "core/dispatch.hpp"
|
||||
#include "core/framerate.hpp"
|
||||
#include "core/keymapper.hpp"
|
||||
#include "core/misc.hpp"
|
||||
#include "core/settings.hpp"
|
||||
#include "core/window.hpp"
|
||||
#include "library/framebuffer.hpp"
|
||||
#include "library/minmax.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <csignal>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <cassert>
|
||||
|
||||
#define WATCHDOG_TIMEOUT 15
|
||||
#define MAXMESSAGES 6
|
||||
#define MSGHISTORY 1000
|
||||
#define MAXHISTORY 1000
|
||||
|
||||
#include <SDL.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace
|
||||
{
|
||||
SDL_AudioSpec* desired;
|
||||
SDL_AudioSpec* obtained;
|
||||
uint32_t audio_playback_freq = 0;
|
||||
volatile double sample_rate = 32000;
|
||||
Uint16 format = AUDIO_S16SYS;
|
||||
bool stereo = true;
|
||||
bool sound_enabled = true;
|
||||
|
||||
|
||||
void audiocb(void* dummy, Uint8* stream, int len)
|
||||
{
|
||||
const unsigned voice_blocksize = 128;
|
||||
int16_t voicebuf[voice_blocksize];
|
||||
if(format == AUDIO_S8 || format == AUDIO_U8)
|
||||
len /= (stereo ? 2 : 1);
|
||||
else
|
||||
len /= (stereo ? 4 : 2);
|
||||
|
||||
size_t ptr = 0;
|
||||
while(len > 0) {
|
||||
unsigned bsize = min(voice_blocksize / (stereo ? 2 : 1), static_cast<unsigned>(len));
|
||||
audioapi_get_mixed(voicebuf, bsize, stereo);
|
||||
if(format == AUDIO_S16LSB) {
|
||||
for(size_t i = 0; i < bsize * (stereo ? 2 : 1); i++) {
|
||||
stream[ptr++] = static_cast<uint16_t>(voicebuf[i]);
|
||||
stream[ptr++] = static_cast<uint16_t>(voicebuf[i]) >> 8;
|
||||
}
|
||||
} else if(format == AUDIO_U16LSB) {
|
||||
for(size_t i = 0; i < bsize * (stereo ? 2 : 1); i++) {
|
||||
stream[ptr++] = static_cast<uint16_t>(voicebuf[i]);
|
||||
stream[ptr++] = (static_cast<uint16_t>(voicebuf[i]) + 32768) >> 8;
|
||||
}
|
||||
} else if(format == AUDIO_S16MSB) {
|
||||
for(size_t i = 0; i < bsize * (stereo ? 2 : 1); i++) {
|
||||
stream[ptr++] = static_cast<uint16_t>(voicebuf[i]) >> 8;
|
||||
stream[ptr++] = static_cast<uint16_t>(voicebuf[i]);
|
||||
}
|
||||
} else if(format == AUDIO_U16MSB) {
|
||||
for(size_t i = 0; i < bsize * (stereo ? 2 : 1); i++) {
|
||||
stream[ptr++] = (static_cast<uint16_t>(voicebuf[i]) + 32768) >> 8;
|
||||
stream[ptr++] = static_cast<uint16_t>(voicebuf[i]);
|
||||
}
|
||||
} else if(format == AUDIO_S16SYS) {
|
||||
int16_t* _stream = reinterpret_cast<int16_t*>(stream);
|
||||
for(size_t i = 0; i < bsize * (stereo ? 2 : 1); i++)
|
||||
_stream[ptr++] = voicebuf[i];
|
||||
} else if(format == AUDIO_U16SYS) {
|
||||
uint16_t* _stream = reinterpret_cast<uint16_t*>(stream);
|
||||
for(size_t i = 0; i < bsize * (stereo ? 2 : 1); i++)
|
||||
_stream[ptr++] = voicebuf[i] + 32768;
|
||||
} else if(format == AUDIO_S8) {
|
||||
for(size_t i = 0; i < bsize * (stereo ? 2 : 1); i++)
|
||||
stream[ptr++] = static_cast<uint16_t>(voicebuf[i]) >> 8;
|
||||
} else if(format == AUDIO_U8) {
|
||||
for(size_t i = 0; i < bsize * (stereo ? 2 : 1); i++)
|
||||
stream[ptr++] = (static_cast<uint16_t>(voicebuf[i]) + 32768) >> 8;
|
||||
}
|
||||
audioapi_put_voice(NULL, bsize);
|
||||
len -= bsize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void audioapi_driver_enable(bool enable) throw()
|
||||
{
|
||||
sound_enabled = enable;
|
||||
SDL_PauseAudio(enable ? 0 : 1);
|
||||
audioapi_set_dummy_cb(!enable);
|
||||
if(enable)
|
||||
audioapi_voice_rate(audio_playback_freq);
|
||||
}
|
||||
|
||||
void audioapi_driver_init() throw()
|
||||
{
|
||||
desired = new SDL_AudioSpec();
|
||||
obtained = new SDL_AudioSpec();
|
||||
|
||||
desired->freq = 44100;
|
||||
desired->format = AUDIO_S16SYS;
|
||||
desired->channels = 2;
|
||||
desired->samples = 2048;
|
||||
desired->callback = audiocb;
|
||||
desired->userdata = NULL;
|
||||
|
||||
auto g = information_dispatch::get_sound_rate();
|
||||
sample_rate = g.first * 1.0 / g.second;
|
||||
|
||||
if(SDL_OpenAudio(desired, obtained) < 0) {
|
||||
platform::message("Audio can't be initialized, audio playback disabled");
|
||||
//Disable audio.
|
||||
audio_playback_freq = 0;
|
||||
audioapi_set_dummy_cb(true);
|
||||
return;
|
||||
}
|
||||
|
||||
//Fill the parameters.
|
||||
audio_playback_freq = obtained->freq;
|
||||
audioapi_voice_rate(audio_playback_freq);
|
||||
format = obtained->format;
|
||||
stereo = (obtained->channels == 2);
|
||||
//GO!!!
|
||||
SDL_PauseAudio(0);
|
||||
}
|
||||
|
||||
void audioapi_driver_quit() throw()
|
||||
{
|
||||
SDL_PauseAudio(1);
|
||||
SDL_Delay(100);
|
||||
delete desired;
|
||||
delete obtained;
|
||||
}
|
||||
|
||||
class sound_change_listener : public information_dispatch
|
||||
{
|
||||
public:
|
||||
sound_change_listener() : information_dispatch("sdl-sound-change-listener") {}
|
||||
void on_sound_rate(uint32_t rate_n, uint32_t rate_d)
|
||||
{
|
||||
}
|
||||
} sndchgl;
|
||||
|
||||
bool audioapi_driver_initialized()
|
||||
{
|
||||
return (audio_playback_freq != 0);
|
||||
}
|
||||
|
||||
void audioapi_driver_set_device(const std::string& dev) throw(std::bad_alloc, std::runtime_error)
|
||||
{
|
||||
if(dev != "default")
|
||||
throw std::runtime_error("Bad sound device '" + dev + "'");
|
||||
}
|
||||
|
||||
std::string audioapi_driver_get_device() throw(std::bad_alloc)
|
||||
{
|
||||
return "default";
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> audioapi_driver_get_devices() throw(std::bad_alloc)
|
||||
{
|
||||
std::map<std::string, std::string> ret;
|
||||
ret["default"] = "default sound output";
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char* audioapi_driver_name = "SDL sound plugin";
|
|
@ -1,391 +0,0 @@
|
|||
#include "core/framebuffer.hpp"
|
||||
#include "fonts/wrapper.hpp"
|
||||
#include "platform/sdl/platform.hpp"
|
||||
|
||||
void statusarea_model::paint(SDL_Surface* surf, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t color,
|
||||
bool box, uint32_t boxcolor) throw()
|
||||
{
|
||||
if(!w || !h)
|
||||
return;
|
||||
try {
|
||||
//Quickly copy the status area.
|
||||
auto& s = platform::get_emustatus();
|
||||
std::map<std::string, std::string> newstatus;
|
||||
emulator_status::iterator i = s.first();
|
||||
while(s.next(i))
|
||||
newstatus[i.key] = i.value;
|
||||
//Trick to do full redraw.
|
||||
if(box) {
|
||||
draw_box(surf, x, y, w, h, boxcolor);
|
||||
current_contents.clear();
|
||||
}
|
||||
//Draw it.
|
||||
uint32_t lines = h / 16;
|
||||
std::map<std::string, std::string>::iterator old_itr = current_contents.begin();
|
||||
std::map<std::string, std::string>::iterator new_itr = newstatus.begin();
|
||||
for(uint32_t j = 0; j < lines; j++) {
|
||||
std::string s;
|
||||
if(new_itr == newstatus.end())
|
||||
draw_string(surf, "", x, y + 16 * j, w, color);
|
||||
else if(old_itr == current_contents.end() || old_itr->first != new_itr->first ||
|
||||
old_itr->second != new_itr->second) {
|
||||
s = new_itr->first + " " + new_itr->second;
|
||||
draw_string(surf, s, x, y + 16 * j, w, color);
|
||||
}
|
||||
if(old_itr != current_contents.end())
|
||||
old_itr++;
|
||||
if(new_itr != newstatus.end())
|
||||
new_itr++;
|
||||
}
|
||||
current_contents = newstatus;
|
||||
} catch(std::bad_alloc& e) {
|
||||
OOM_panic();
|
||||
}
|
||||
}
|
||||
|
||||
messages_model::messages_model() throw()
|
||||
{
|
||||
first_visible = 0;
|
||||
messages_visible = 0;
|
||||
}
|
||||
|
||||
void messages_model::paint(SDL_Surface* surf, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t color,
|
||||
bool box, uint32_t boxcolor) throw()
|
||||
{
|
||||
if(!w || !h)
|
||||
return;
|
||||
try {
|
||||
//Trick to do full redraw.
|
||||
if(box) {
|
||||
draw_box(surf, x, y, w, h, boxcolor);
|
||||
first_visible = 0;
|
||||
messages_visible = 0;
|
||||
}
|
||||
//Quickly copy the messages we need
|
||||
std::vector<std::string> msgs;
|
||||
uint32_t old_visible_count = messages_visible;
|
||||
uint32_t old_painted = messages_visible;
|
||||
uint32_t lines = h / 16;
|
||||
{
|
||||
uint64_t old_first = first_visible;
|
||||
mutex::holder h(platform::msgbuf_lock());
|
||||
first_visible = platform::msgbuf.get_visible_first();
|
||||
msgs.resize(messages_visible = platform::msgbuf.get_visible_count());
|
||||
if(old_first == first_visible && messages_visible == old_visible_count)
|
||||
return; //Up to date.
|
||||
for(size_t i = 0; i < messages_visible; i++)
|
||||
msgs[i] = platform::msgbuf.get_message(first_visible + i);
|
||||
//If first changes, we have to repaint all.
|
||||
if(old_first != first_visible)
|
||||
old_visible_count = 0;
|
||||
}
|
||||
//Draw messages.
|
||||
for(uint32_t i = 0; i < lines; i++) {
|
||||
if(i < old_visible_count || (i >= old_painted && i >= messages_visible))
|
||||
continue; //Up to date.
|
||||
else if(i < messages_visible) {
|
||||
//This is a new message.
|
||||
std::ostringstream s;
|
||||
s << (first_visible + i) << ": " << msgs[i];
|
||||
draw_string(surf, s.str(), x, y + 16 * i, w, color);
|
||||
} else {
|
||||
//Blank line to paint.
|
||||
draw_string(surf, "", x, y + 16 * i, w, color);
|
||||
}
|
||||
}
|
||||
} catch(std::bad_alloc& e) {
|
||||
OOM_panic();
|
||||
}
|
||||
}
|
||||
|
||||
void paint_modal_dialog(SDL_Surface* surf, const std::string& text, bool confirm, uint32_t color) throw()
|
||||
{
|
||||
try {
|
||||
std::string rtext;
|
||||
if(confirm)
|
||||
rtext = text + "\n\nPress Enter to confirm or Escape to cancel.";
|
||||
else
|
||||
rtext = text + "\n\nPress Enter or Escape to dismiss.";
|
||||
//Find the dimensions of the text.
|
||||
uint32_t text_w = 0;
|
||||
uint32_t text_h = 0;
|
||||
int32_t x = 0;
|
||||
int32_t y = 0;
|
||||
auto s2 = decode_utf8(rtext);
|
||||
for(auto i : s2) {
|
||||
auto g = main_font.get_glyph(i);
|
||||
if(i == 9)
|
||||
x = (x + 63) / 64 * 64;
|
||||
else if(i == 10) {
|
||||
x = 0;
|
||||
y += 16;
|
||||
} else {
|
||||
x += (g.wide ? 16 : 8);
|
||||
}
|
||||
if(x + (g.wide ? 16 : 8) > text_w)
|
||||
text_w = static_cast<uint32_t>(x + (g.wide ? 16 : 8));
|
||||
if(y + 16 > static_cast<int32_t>(text_h))
|
||||
text_h = static_cast<uint32_t>(y + 16);
|
||||
}
|
||||
uint32_t x1;
|
||||
uint32_t x2;
|
||||
uint32_t y1;
|
||||
uint32_t y2;
|
||||
if(text_w + 12 >= static_cast<uint32_t>(surf->w)) {
|
||||
x1 = 6;
|
||||
x2 = surf->w - 6;
|
||||
text_w = x2 - x1;
|
||||
} else {
|
||||
x1 = (surf->w - text_w) / 2;
|
||||
x2 = x1 + text_w;
|
||||
}
|
||||
if(text_h + 12 >= static_cast<uint32_t>(surf->h)) {
|
||||
y1 = 6;
|
||||
y2 = surf->h - 6;
|
||||
text_h = y2 - y1;
|
||||
} else {
|
||||
y1 = (surf->h - text_h) / 2;
|
||||
y2 = y1 + text_h;
|
||||
}
|
||||
draw_box(surf, x1, y1, text_w, text_h, color);
|
||||
//Draw each line.
|
||||
for(uint32_t j = 0; j < text_h / 16; j++) {
|
||||
std::string thisline;
|
||||
std::string rest;
|
||||
size_t split = rtext.find_first_of("\n");
|
||||
if(split < rtext.length()) {
|
||||
thisline = rtext.substr(0, split);
|
||||
rest = rtext.substr(split + 1);
|
||||
} else
|
||||
thisline = rtext;
|
||||
rtext = rest;
|
||||
draw_string(surf, thisline, x1, y1 + 16 * j, text_w, color);
|
||||
}
|
||||
} catch(std::bad_alloc& e) {
|
||||
OOM_panic();
|
||||
}
|
||||
}
|
||||
|
||||
#define MIN_WIDTH 512
|
||||
#define DEFAULT_WIDTH 512
|
||||
#define MIN_HEIGHT 448
|
||||
#define DEFAULT_HEIGHT 448
|
||||
#define STATUS_WIDTH 256
|
||||
#define MESSAGES_LINES 6
|
||||
|
||||
screen_layout::screen_layout()
|
||||
{
|
||||
real_width = DEFAULT_WIDTH;
|
||||
real_height = DEFAULT_HEIGHT;
|
||||
fullscreen_console = false;
|
||||
update();
|
||||
}
|
||||
|
||||
void screen_layout::update() throw()
|
||||
{
|
||||
uint32_t w = (real_width < MIN_WIDTH) ? MIN_WIDTH : real_width;
|
||||
uint32_t h = (real_height < MIN_HEIGHT) ? MIN_HEIGHT : real_height;
|
||||
window_w = w + 22 + STATUS_WIDTH;
|
||||
window_h = h + 48 + 16 * MESSAGES_LINES;
|
||||
|
||||
screen_x = 6;
|
||||
screen_y = 6;
|
||||
status_x = 16 + w;
|
||||
status_y = 6;
|
||||
messages_x = 6;
|
||||
messages_w = window_w - 12;
|
||||
commandline_x = 6;
|
||||
commandline_y = window_h - 22;
|
||||
commandline_w = window_w - 12;
|
||||
|
||||
if(fullscreen_console) {
|
||||
screen_w = screen_h = 0;
|
||||
status_w = status_h = 0;
|
||||
messages_y = 6;
|
||||
messages_h = window_h - 38;
|
||||
} else {
|
||||
screen_w = w;
|
||||
screen_h = h;
|
||||
status_w = window_w - 22 - w;
|
||||
status_h = h;
|
||||
messages_y = 16 + h;
|
||||
messages_h = window_h - 48 - h;
|
||||
}
|
||||
mutex::holder hx(platform::msgbuf_lock());
|
||||
platform::msgbuf.set_max_window_size(messages_h / 16);
|
||||
}
|
||||
|
||||
void screen_model::set_command_line(commandline_model* c) throw()
|
||||
{
|
||||
cmdline = c;
|
||||
repaint_commandline();
|
||||
}
|
||||
|
||||
void screen_model::clear_modal() throw()
|
||||
{
|
||||
modal_active = false;
|
||||
modal_confirm = false;
|
||||
modal_text = "";
|
||||
repaint_full();
|
||||
}
|
||||
|
||||
void screen_model::set_modal(const std::string& text, bool confirm) throw()
|
||||
{
|
||||
try {
|
||||
modal_active = true;
|
||||
modal_confirm = confirm;
|
||||
modal_text = text;
|
||||
repaint_modal();
|
||||
} catch(std::bad_alloc& e) {
|
||||
OOM_panic();
|
||||
}
|
||||
}
|
||||
|
||||
void screen_model::set_fullscreen_console(bool enable) throw()
|
||||
{
|
||||
layout.fullscreen_console = enable;
|
||||
layout.update();
|
||||
repaint_full();
|
||||
}
|
||||
|
||||
void screen_model::repaint_commandline() throw()
|
||||
{
|
||||
if(!surf)
|
||||
return repaint_full();
|
||||
else if(cmdline) {
|
||||
SDL_LockSurface(surf);
|
||||
cmdline->paint(surf, layout.commandline_x, layout.commandline_y, layout.commandline_w, 0xFFFFFFFFUL);
|
||||
SDL_UnlockSurface(surf);
|
||||
}
|
||||
}
|
||||
|
||||
void screen_model::repaint_messages() throw()
|
||||
{
|
||||
if(!surf)
|
||||
return repaint_full();
|
||||
else {
|
||||
SDL_LockSurface(surf);
|
||||
smessages.paint(surf, layout.messages_x, layout.messages_y, layout.messages_w, layout.messages_h,
|
||||
0xFFFFFFFFUL);
|
||||
SDL_UnlockSurface(surf);
|
||||
}
|
||||
}
|
||||
|
||||
void screen_model::repaint_status() throw()
|
||||
{
|
||||
if(!surf)
|
||||
return repaint_full();
|
||||
else {
|
||||
SDL_LockSurface(surf);
|
||||
statusarea.paint(surf, layout.status_x, layout.status_y, layout.status_w, layout.status_h,
|
||||
0xFFFFFFFFUL);
|
||||
SDL_UnlockSurface(surf);
|
||||
}
|
||||
}
|
||||
|
||||
void screen_model::repaint_screen() throw()
|
||||
{
|
||||
if(!surf)
|
||||
repaint_full();
|
||||
else
|
||||
_repaint_screen();
|
||||
}
|
||||
|
||||
void screen_model::flip() throw()
|
||||
{
|
||||
if(!surf)
|
||||
repaint_full();
|
||||
SDL_Flip(surf);
|
||||
}
|
||||
|
||||
screen_model::screen_model()
|
||||
{
|
||||
surf = NULL;
|
||||
cmdline = NULL;
|
||||
modal_active = false;
|
||||
modal_confirm = false;
|
||||
old_screen_h = 0;
|
||||
old_screen_w = 0;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void do_set_palette(void* x)
|
||||
{
|
||||
screen_model* m = reinterpret_cast<screen_model*>(x);
|
||||
platform::screen_set_palette(m->pal_r, m->pal_g, m->pal_b);
|
||||
}
|
||||
}
|
||||
|
||||
void screen_model::repaint_full() throw()
|
||||
{
|
||||
render_framebuffer();
|
||||
uint32_t current_w = main_screen.get_width();
|
||||
uint32_t current_h = main_screen.get_height();
|
||||
if(!surf || old_screen_w != current_w || old_screen_h != current_h) {
|
||||
layout.real_width = current_w;
|
||||
layout.real_height = current_h;
|
||||
layout.update();
|
||||
surf = SDL_SetVideoMode(layout.window_w, layout.window_h, 32, SDL_SWSURFACE | SDL_DOUBLEBUF);
|
||||
if(!surf) {
|
||||
//We are fucked.
|
||||
std::cerr << "Can't set video mode: " << SDL_GetError() << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
old_screen_w = current_w;
|
||||
old_screen_h = current_h;
|
||||
pal_r = surf->format->Rshift;
|
||||
pal_g = surf->format->Gshift;
|
||||
pal_b = surf->format->Bshift;
|
||||
platform::queue(do_set_palette, this, false);
|
||||
}
|
||||
SDL_LockSurface(surf);
|
||||
if(cmdline)
|
||||
cmdline->paint(surf, layout.commandline_x, layout.commandline_y, layout.commandline_w, 0xFFFFFFFFUL,
|
||||
true, surf->format->Gmask);
|
||||
smessages.paint(surf, layout.messages_x, layout.messages_y, layout.messages_w, layout.messages_h,
|
||||
0xFFFFFFFFUL, true, surf->format->Gmask);
|
||||
statusarea.paint(surf, layout.status_x, layout.status_y, layout.status_w, layout.status_h,
|
||||
0xFFFFFFFFUL, true, surf->format->Gmask);
|
||||
draw_box(surf, layout.screen_x, layout.screen_y, layout.screen_w, layout.screen_h, surf->format->Gmask);
|
||||
if(layout.screen_w && layout.screen_h) {
|
||||
for(uint32_t i = 0; i < current_h; i++)
|
||||
memcpy(reinterpret_cast<uint8_t*>(surf->pixels) + (i + layout.screen_y) * surf->pitch +
|
||||
4 * layout.screen_x, main_screen.rowptr(i), 4 * current_w);
|
||||
}
|
||||
SDL_UnlockSurface(surf);
|
||||
if(modal_active)
|
||||
repaint_modal();
|
||||
}
|
||||
|
||||
void screen_model::repaint_modal() throw()
|
||||
{
|
||||
if(!surf)
|
||||
return repaint_full();
|
||||
else {
|
||||
SDL_LockSurface(surf);
|
||||
paint_modal_dialog(surf, modal_text, modal_confirm, surf->format->Rmask | (surf->format->Gmask >> 1));
|
||||
SDL_UnlockSurface(surf);
|
||||
}
|
||||
}
|
||||
|
||||
void screen_model::_repaint_screen() throw()
|
||||
{
|
||||
render_framebuffer();
|
||||
uint32_t current_w = main_screen.get_width();
|
||||
uint32_t current_h = main_screen.get_height();
|
||||
//Optimize for case where the screen is not resized.
|
||||
{
|
||||
SDL_LockSurface(surf);
|
||||
if(!surf || old_screen_w != current_w || old_screen_h != current_h)
|
||||
goto fail;
|
||||
for(uint32_t i = 0; i < current_h; i++)
|
||||
memcpy(reinterpret_cast<uint8_t*>(surf->pixels) + (i + layout.screen_y) * surf->pitch +
|
||||
4 * layout.screen_x, main_screen.rowptr(i), 4 * current_w);
|
||||
SDL_UnlockSurface(surf);
|
||||
}
|
||||
return;
|
||||
fail:
|
||||
SDL_UnlockSurface(surf);
|
||||
repaint_full();
|
||||
}
|
|
@ -1,210 +0,0 @@
|
|||
#include "core/window.hpp"
|
||||
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_thread.h>
|
||||
|
||||
struct sdl_mutex : public mutex
|
||||
{
|
||||
sdl_mutex() throw(std::bad_alloc);
|
||||
~sdl_mutex() throw();
|
||||
void lock() throw();
|
||||
void unlock() throw();
|
||||
SDL_mutex* m;
|
||||
};
|
||||
|
||||
struct sdl_rec_mutex : public mutex
|
||||
{
|
||||
sdl_rec_mutex() throw(std::bad_alloc);
|
||||
~sdl_rec_mutex() throw();
|
||||
void lock() throw();
|
||||
void unlock() throw();
|
||||
SDL_mutex* m;
|
||||
volatile bool locked;
|
||||
uint32_t owner;
|
||||
uint32_t count;
|
||||
};
|
||||
|
||||
sdl_mutex::sdl_mutex() throw(std::bad_alloc)
|
||||
{
|
||||
m = SDL_CreateMutex();
|
||||
if(!m)
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
sdl_mutex::~sdl_mutex() throw()
|
||||
{
|
||||
SDL_DestroyMutex(m);
|
||||
}
|
||||
|
||||
sdl_rec_mutex::sdl_rec_mutex() throw(std::bad_alloc)
|
||||
{
|
||||
m = SDL_CreateMutex();
|
||||
if(!m)
|
||||
throw std::bad_alloc();
|
||||
locked = false;
|
||||
owner = 0;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
sdl_rec_mutex::~sdl_rec_mutex() throw()
|
||||
{
|
||||
SDL_DestroyMutex(m);
|
||||
}
|
||||
|
||||
void sdl_mutex::lock() throw()
|
||||
{
|
||||
SDL_mutexP(m);
|
||||
}
|
||||
|
||||
void sdl_mutex::unlock() throw()
|
||||
{
|
||||
SDL_mutexV(m);
|
||||
}
|
||||
|
||||
void sdl_rec_mutex::lock() throw()
|
||||
{
|
||||
uint32_t our_id = SDL_ThreadID();
|
||||
if(locked && owner == our_id) {
|
||||
//Owned by us, increment lock count.
|
||||
++count;
|
||||
return;
|
||||
}
|
||||
SDL_mutexP(m);
|
||||
locked = true;
|
||||
owner = our_id;
|
||||
count = 1;
|
||||
}
|
||||
|
||||
void sdl_rec_mutex::unlock() throw()
|
||||
{
|
||||
uint32_t our_id = SDL_ThreadID();
|
||||
if(!locked || owner != our_id)
|
||||
std::cerr << "Warning: Trying to unlock recursive lock locked by another thread!" << std::endl;
|
||||
if(!--count) {
|
||||
locked = false;
|
||||
owner = 0;
|
||||
SDL_mutexV(m);
|
||||
}
|
||||
}
|
||||
|
||||
mutex& mutex::aquire() throw(std::bad_alloc)
|
||||
{
|
||||
return *new sdl_mutex;
|
||||
}
|
||||
|
||||
mutex& mutex::aquire_rec() throw(std::bad_alloc)
|
||||
{
|
||||
return *new sdl_rec_mutex;
|
||||
}
|
||||
|
||||
struct sdl_condition : public condition
|
||||
{
|
||||
sdl_condition(mutex& m) throw(std::bad_alloc);
|
||||
~sdl_condition() throw();
|
||||
bool wait(uint64_t x) throw();
|
||||
void signal() throw();
|
||||
SDL_cond* c;
|
||||
};
|
||||
|
||||
sdl_condition::sdl_condition(mutex& m) throw(std::bad_alloc)
|
||||
: condition(m)
|
||||
{
|
||||
c = SDL_CreateCond();
|
||||
if(!c)
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
sdl_condition::~sdl_condition() throw()
|
||||
{
|
||||
SDL_DestroyCond(c);
|
||||
}
|
||||
|
||||
bool sdl_condition::wait(uint64_t x) throw()
|
||||
{
|
||||
sdl_mutex* m = dynamic_cast<sdl_mutex*>(&associated());
|
||||
if(!m)
|
||||
return false;
|
||||
return (SDL_CondWaitTimeout(c, m->m, (x + 999) / 1000) == 0);
|
||||
}
|
||||
|
||||
void sdl_condition::signal() throw()
|
||||
{
|
||||
SDL_CondBroadcast(c);
|
||||
}
|
||||
|
||||
condition& condition::aquire(mutex& m) throw(std::bad_alloc)
|
||||
{
|
||||
return *new sdl_condition(m);
|
||||
}
|
||||
|
||||
struct sdl_thread_id : public thread_id
|
||||
{
|
||||
sdl_thread_id() throw();
|
||||
~sdl_thread_id() throw();
|
||||
bool is_me() throw();
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
sdl_thread_id::sdl_thread_id() throw()
|
||||
{
|
||||
id = SDL_ThreadID();
|
||||
}
|
||||
|
||||
|
||||
sdl_thread_id::~sdl_thread_id() throw()
|
||||
{
|
||||
}
|
||||
|
||||
bool sdl_thread_id::is_me() throw()
|
||||
{
|
||||
return (id == SDL_ThreadID());
|
||||
}
|
||||
|
||||
thread_id& thread_id::me() throw(std::bad_alloc)
|
||||
{
|
||||
return *new sdl_thread_id;
|
||||
}
|
||||
|
||||
struct sdl_thread : public thread
|
||||
{
|
||||
sdl_thread(void* (*fn)(void* arg), void* arg) throw(std::runtime_error);
|
||||
~sdl_thread() throw();
|
||||
void _join() throw();
|
||||
void* (*entry)(void* arg);
|
||||
void* entry_arg;
|
||||
static int sdl_entrypoint(void* arg);
|
||||
SDL_Thread* handle;
|
||||
};
|
||||
|
||||
int sdl_thread::sdl_entrypoint(void* arg)
|
||||
{
|
||||
sdl_thread* t = reinterpret_cast<sdl_thread*>(arg);
|
||||
t->notify_quit(t->entry(t->entry_arg));
|
||||
}
|
||||
|
||||
sdl_thread::sdl_thread(void* (*fn)(void* arg), void* arg) throw(std::runtime_error)
|
||||
{
|
||||
entry = fn;
|
||||
entry_arg = arg;
|
||||
handle = SDL_CreateThread(sdl_entrypoint, this);
|
||||
if(!handle)
|
||||
throw std::runtime_error("Can't create thread");
|
||||
}
|
||||
|
||||
sdl_thread::~sdl_thread() throw()
|
||||
{
|
||||
_join();
|
||||
}
|
||||
|
||||
void sdl_thread::_join() throw()
|
||||
{
|
||||
if(handle)
|
||||
SDL_WaitThread(handle, NULL);
|
||||
handle = NULL;
|
||||
}
|
||||
|
||||
thread& thread::create(void* (*entrypoint)(void* arg), void* arg) throw(std::bad_alloc, std::runtime_error)
|
||||
{
|
||||
return *new sdl_thread(entrypoint, arg);
|
||||
}
|
Loading…
Add table
Reference in a new issue