Remove obsolete SDL stuff

This commit is contained in:
Ilari Liusvaara 2012-12-16 21:06:26 +02:00
parent 690c561d2d
commit 7b0b39d536
20 changed files with 61 additions and 4401 deletions

View file

@ -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

View file

@ -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

View file

@ -721,117 +721,6 @@ run-script <script>
Run <script> as if commands were entered on the command line. Run <script> as if commands were entered on the command line.
\end_layout \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 \begin_layout Subsection
Memory manipulation Memory manipulation
\end_layout \end_layout
@ -1418,30 +1307,6 @@ enable-sound <on/off>
Enable/Disable sound. Enable/Disable sound.
\end_layout \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 \begin_layout Subsubsection
set-volume <multiplier> set-volume <multiplier>
\end_layout \end_layout
@ -1482,119 +1347,6 @@ reload-rom [<file>]
Reloads the main ROM image from <file>. Reloads the main ROM image from <file>.
\end_layout \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 \begin_layout Subsubsection
+tangent +tangent
\end_layout \end_layout
@ -1604,70 +1356,6 @@ Tangent for recording voice for commentary track.
While pressed, record a stream. While pressed, record a stream.
\end_layout \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 \begin_layout Section
Settings Settings
\end_layout \end_layout

View file

@ -333,60 +333,7 @@ Print all aliases and their expansions in effect.
Run <script> as if commands were entered on the command line. Run <script> as if commands were entered on the command line.
6.5 Video dumping 6.5 Memory manipulation
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
<address> may be decimal or hexadecimal (prefixed with '0x'). <address> may be decimal or hexadecimal (prefixed with '0x').
<value> can be hexadecimal (prefixed with '0x'), unsigned or <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, native-endian (do not use operand sizes exceeding DSP bitness,
except dword is OK for 24-bit memory). 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>. 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>. 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>. Write <value> to byte in address <address>.
6.6.4 search-memory reset 6.5.4 search-memory reset
Reset the memory search Reset the memory search
6.6.5 search-memory count 6.5.5 search-memory count
Print number of candidates remaining Print number of candidates remaining
6.6.6 search-memory print 6.5.6 search-memory print
Print all candidates remaining 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. Searches memory for addresses satisfying criteria.
@ -465,122 +412,122 @@ Searches memory for addresses satisfying criteria.
• gt: > previous value. • 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>. Searches for addresses that currently have value <value>.
<sizeflag> is as in previous command. <sizeflag> is as in previous command.
6.7 Main commands 6.6 Main commands
These commands are not available in lsnesrc, but are available These commands are not available in lsnesrc, but are available
after ROM has been loaded. 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 Quits the emulator (asking for confirmation). If /y is given, no
confirmation is asked. confirmation is asked.
6.7.2 pause-emulator 6.6.2 pause-emulator
Toggle paused/unpaused Toggle paused/unpaused
6.7.3 +advance-frame 6.6.3 +advance-frame
Advance frame. If the button is still held after configurable Advance frame. If the button is still held after configurable
timeout expires, game unpauses for the duration frame advance is timeout expires, game unpauses for the duration frame advance is
held. held.
6.7.4 +advance-poll 6.6.4 +advance-poll
Advance subframe. If the button is still held after configurable Advance subframe. If the button is still held after configurable
timeout expires, game unpauses for the duration frame advance is timeout expires, game unpauses for the duration frame advance is
held. held.
6.7.5 advance-skiplag 6.6.5 advance-skiplag
Skip to first poll in frame after current. Skip to first poll in frame after current.
6.7.6 reset 6.6.6 reset
Reset the SNES after this frame. Reset the SNES after this frame.
6.7.7 load <filename> 6.6.7 load <filename>
Load savestate <filename> in current mode. Load savestate <filename> in current mode.
6.7.8 load-state <filename> 6.6.8 load-state <filename>
Load savestate <filename> in readwrite mode. Load savestate <filename> in readwrite mode.
6.7.9 load-readonly <filename> 6.6.9 load-readonly <filename>
Load savestate <filename> in readonly mode. 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 Load savestate <filename> in readonly mode, preserving current
events. events.
6.7.11 load-movie <filename> 6.6.11 load-movie <filename>
Load savestate <filename>, ignoring save part in readonly mode. 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. 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>. Save movie to <filename>.
6.7.14 set-rwmode 6.6.14 set-rwmode
Set read-write mode. Set read-write mode.
6.7.15 set-romode 6.6.15 set-romode
Set read-only mode Set read-only mode
6.7.16 toggle-rwmode 6.6.16 toggle-rwmode
Toggle between read-only and read-write modes. 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> Set name of the game to <name>
6.7.18 get-gamename 6.6.18 get-gamename
Print the name of the game. 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 Adds new author <author>. If <author> does not contain '|' it is
full name. If it contains '|', '|' splits the full name and full name. If it contains '|', '|' splits the full name and
nickname. 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 Edit the author in slot <num> (0-based) to be <author> (see
add-author for format) add-author for format)
6.7.21 remove-author <num> 6.6.21 remove-author <num>
Remove author in slot <num> Remove author in slot <num>
6.7.22 print-authors 6.6.22 print-authors
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. Internal test commands. Don't use.
6.7.24 take-screenshot <filename> 6.6.24 take-screenshot <filename>
Save screenshot to <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 Press button <button> on controller <num> (1-8). The following
button names are known: button names are known:
@ -617,209 +564,105 @@ button names are known:
• turbo • turbo
6.7.26 controllerh<num><button> 6.6.26 controllerh<num><button>
Hold/unhold button <button> on controller <num> (1-8). See Hold/unhold button <button> on controller <num> (1-8). See
+controller for button names. +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 Hold/unhold button <button> on controller <num> (1-8) for the
next frame. See +controller for button names. next frame. See +controller for button names.
Cauntion: Does not work properly if outside frame advance. 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 Set autofire pattern. Each parameter is comma-separated list of
button names (in form of 1start, 1A, 2B, etc..) to hold on that button names (in form of 1start, 1A, 2B, etc..) to hold on that
frame. After reaching the end of pattern, the pattern restarts frame. After reaching the end of pattern, the pattern restarts
from the beginning. from the beginning.
6.7.29 repaint 6.6.29 repaint
Force a 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. Cycle save jukebox backwards.
6.8.2 cycle-jukebox-forward 6.7.2 cycle-jukebox-forward
Cycle save jukebox forwards Cycle save jukebox forwards
6.8.3 load-jukebox 6.7.3 load-jukebox
Do load from jukebox (current mode). Do load from jukebox (current mode).
6.8.4 save-jukebox 6.7.4 save-jukebox
Do state save to jukebox. Do state save to jukebox.
6.9 Lua 6.8 Lua
Only available if lua support is compiled in. 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. 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. 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. 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). Adds new watch (or modifies old one).
6.10.2 remove-watch <name> 6.9.2 remove-watch <name>
Remove a watch. 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. Enable/Disable sound.
6.11.2 set-sound-device <device> 6.10.2 set-volume <multiplier>
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>
Set the volume multiplier to <multiplier>. 1 is normal volume, Set the volume multiplier to <multiplier>. 1 is normal volume,
and higher numbers are louder. 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 Set the volume multiplier to <multiplier> percent. 100 is normal
volume, and higher numbers are louder. 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, Set the volume multiplier to <multiplier> dB. 0 is normal volume,
and higher numbers are louder. The value may be negative. 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>. Reloads the main ROM image from <file>.
6.12.2 edit-subtitle <firstframe> <length> [<text>] 6.11.2 +tangent
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
Tangent for recording voice for commentary track. While pressed, Tangent for recording voice for commentary track. While pressed,
record a stream. 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 Settings
7.1 Core settings 7.1 Core settings

View file

@ -11,84 +11,6 @@
namespace namespace
{ {
globalwrap<std::map<std::string, adv_dumper*>> dumpers; 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() const std::string& adv_dumper::id() throw()

View file

@ -1802,8 +1802,8 @@ out:
[]() throw(std::bad_alloc, std::runtime_error) { []() throw(std::bad_alloc, std::runtime_error) {
active_flag = false; active_flag = false;
}); });
inverse_key itangent("+tangent", "Movie‣Voice tangent");
inthread_th* int_task; inthread_th* int_task;
} }
void voice_frame_number(uint64_t newframe, double rate) 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); 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() bool voicesub_collection_loaded()
{ {
umutex_class m2(current_collection_lock); umutex_class m2(current_collection_lock);

View file

@ -156,47 +156,6 @@ namespace
inverse_key ienable_sound("enable-sound on", "Sound‣Enable"); inverse_key ienable_sound("enable-sound on", "Sound‣Enable");
inverse_key idisable_sound("enable-sound off", "Sound‣Disable"); 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", 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", "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) { [](const std::string& value) throw(std::bad_alloc, std::runtime_error) {

View file

@ -1,4 +1,4 @@
PLATFORMS=dummy evdev portaudio sdl wxwidgets win32mm libao PLATFORMS=dummy evdev portaudio wxwidgets win32mm libao
ALLOBJECT=__all__.$(OBJECT_SUFFIX) ALLOBJECT=__all__.$(OBJECT_SUFFIX)
ALLFLAGS=__all__.ldflags ALLFLAGS=__all__.ldflags
PLATFORMS_OBJS=$(patsubst %,%/$(ALLOBJECT),$(PLATFORMS)) PLATFORMS_OBJS=$(patsubst %,%/$(ALLOBJECT),$(PLATFORMS))
@ -23,9 +23,6 @@ portaudio/$(ALLOBJECT): forcelook
libao/$(ALLOBJECT): forcelook libao/$(ALLOBJECT): forcelook
$(MAKE) -C libao $(MAKE) -C libao
sdl/$(ALLOBJECT): forcelook
$(MAKE) -C sdl
wxwidgets/$(ALLOBJECT): forcelook wxwidgets/$(ALLOBJECT): forcelook
$(MAKE) -C wxwidgets $(MAKE) -C wxwidgets
@ -38,7 +35,6 @@ precheck:
$(MAKE) -C win32mm precheck $(MAKE) -C win32mm precheck
$(MAKE) -C portaudio precheck $(MAKE) -C portaudio precheck
$(MAKE) -C libao precheck $(MAKE) -C libao precheck
$(MAKE) -C sdl precheck
$(MAKE) -C wxwidgets precheck $(MAKE) -C wxwidgets precheck
clean: clean:
@ -48,7 +44,6 @@ clean:
$(MAKE) -C win32mm clean $(MAKE) -C win32mm clean
$(MAKE) -C portaudio clean $(MAKE) -C portaudio clean
$(MAKE) -C libao clean $(MAKE) -C libao clean
$(MAKE) -C sdl clean
$(MAKE) -C wxwidgets clean $(MAKE) -C wxwidgets clean
forcelook: forcelook:

View file

@ -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

View file

@ -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();
}
}

View file

@ -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);
}

View file

@ -1 +0,0 @@
char SYMBOL_1243228960869283359802095839623673287633;

View file

@ -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";

View file

@ -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";

View file

@ -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();
}

View file

@ -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;
}

View file

@ -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());
}

View file

@ -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";

View file

@ -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();
}

View file

@ -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);
}