/* sim_video.c: Bitmap video output | |
Copyright (c) 2011-2013, Matt Burke | |
Permission is hereby granted, free of charge, to any person obtaining a | |
copy of this software and associated documentation files (the "Software"), | |
to deal in the Software without restriction, including without limitation | |
the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
and/or sell copies of the Software, and to permit persons to whom the | |
Software is furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in | |
all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
Except as contained in this notice, the name of the author shall not be | |
used in advertising or otherwise to promote the sale, use or other dealings | |
in this Software without prior written authorization from the author. | |
08-Nov-2013 MB Added globals for current mouse status | |
11-Jun-2013 MB First version | |
*/ | |
#if defined(USE_SIM_VIDEO) | |
#include "sim_video.h" | |
t_bool vid_active = FALSE; | |
int32 vid_mouse_xrel = 0; | |
int32 vid_mouse_yrel = 0; | |
int32 vid_cursor_x; | |
int32 vid_cursor_y; | |
t_bool vid_mouse_b1 = FALSE; | |
t_bool vid_mouse_b2 = FALSE; | |
t_bool vid_mouse_b3 = FALSE; | |
#if HAVE_LIBSDL | |
#include <SDL.h> | |
#include <SDL_thread.h> | |
#define EVENT_REDRAW 1 /* redraw event for SDL */ | |
#define EVENT_CLOSE 2 /* close event for SDL */ | |
#define MAX_EVENTS 20 /* max events in queue */ | |
typedef struct { | |
SIM_KEY_EVENT events[MAX_EVENTS]; | |
SDL_sem *sem; | |
int32 head; | |
int32 tail; | |
int32 count; | |
} KEY_EVENT_QUEUE; | |
typedef struct { | |
SIM_MOUSE_EVENT events[MAX_EVENTS]; | |
SDL_sem *sem; | |
int32 head; | |
int32 tail; | |
int32 count; | |
} MOUSE_EVENT_QUEUE; | |
int vid_thread (void* arg); | |
/* | |
Currently there are two separate video implementations which exist | |
due to the fact that libSDL and libSDL2 provide vastly different APIs. | |
libSDL2 is the distinctly better choice going forward, and, given the | |
background thread event digestion, is the only one which will work on | |
OSX. | |
We will abandon libSDL (Version 1) completely when the libSDL2 developer | |
components are readily packaged for most Linux distributions. | |
*/ | |
#if SDL_MAJOR_VERSION == 1 | |
t_bool vid_key_state[SDLK_LAST]; | |
t_bool vid_mouse_captured; | |
int32 vid_width; | |
int32 vid_height; | |
SDL_Surface *vid_image; /* video buffer */ | |
SDL_Surface *vid_window; /* window handle */ | |
SDL_Thread *vid_thread_handle; /* event thread handle */ | |
uint32 vid_mono_palette[2]; | |
KEY_EVENT_QUEUE vid_key_events; /* keyboard events */ | |
MOUSE_EVENT_QUEUE vid_mouse_events; /* mouse events */ | |
DEVICE *vid_dev; | |
t_stat vid_open (DEVICE *dptr, uint32 width, uint32 height) | |
{ | |
if (!vid_active) { | |
vid_active = TRUE; | |
vid_width = width; | |
vid_height = height; | |
vid_mouse_captured = FALSE; | |
vid_mouse_xrel = 0; | |
vid_mouse_yrel = 0; | |
vid_key_events.head = 0; | |
vid_key_events.tail = 0; | |
vid_key_events.count = 0; | |
vid_key_events.sem = SDL_CreateSemaphore (1); | |
vid_mouse_events.head = 0; | |
vid_mouse_events.tail = 0; | |
vid_mouse_events.count = 0; | |
vid_mouse_events.sem = SDL_CreateSemaphore (1); | |
vid_dev = dptr; | |
vid_thread_handle = SDL_CreateThread (vid_thread, NULL); | |
if (vid_thread_handle == NULL) { | |
vid_close (); | |
return SCPE_OPENERR; | |
} | |
sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE, vid_dev, "vid_open() - Success\n"); | |
} | |
return SCPE_OK; | |
} | |
t_stat vid_close (void) | |
{ | |
SDL_Event user_event; | |
int status; | |
if (vid_active) { | |
vid_active = FALSE; | |
if (vid_thread_handle) { | |
sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE, vid_dev, "vid_close()\n"); | |
user_event.type = SDL_USEREVENT; | |
user_event.user.code = EVENT_CLOSE; | |
user_event.user.data1 = NULL; | |
user_event.user.data2 = NULL; | |
SDL_PushEvent (&user_event); | |
SDL_WaitThread (vid_thread_handle, &status); | |
vid_thread_handle = NULL; | |
vid_dev = NULL; | |
} | |
if (vid_mouse_events.sem) { | |
SDL_DestroySemaphore(vid_mouse_events.sem); | |
vid_mouse_events.sem = NULL; | |
} | |
if (vid_key_events.sem) { | |
SDL_DestroySemaphore(vid_key_events.sem); | |
vid_key_events.sem = NULL; | |
} | |
} | |
return SCPE_OK; | |
} | |
t_stat vid_poll_kb (SIM_KEY_EVENT *ev) | |
{ | |
if (SDL_SemTryWait (vid_key_events.sem) == 0) { /* get lock */ | |
if (vid_key_events.count > 0) { /* events in queue? */ | |
*ev = vid_key_events.events[vid_key_events.head++]; | |
vid_key_events.count--; | |
if (vid_key_events.head == MAX_EVENTS) | |
vid_key_events.head = 0; | |
SDL_SemPost (vid_key_events.sem); | |
return SCPE_OK; | |
} | |
SDL_SemPost (vid_key_events.sem); | |
} | |
return SCPE_EOF; | |
} | |
t_stat vid_poll_mouse (SIM_MOUSE_EVENT *ev) | |
{ | |
if (SDL_SemTryWait (vid_mouse_events.sem) == 0) { | |
if (vid_mouse_events.count > 0) { | |
*ev = vid_mouse_events.events[vid_mouse_events.head++]; | |
vid_mouse_events.count--; | |
if (vid_mouse_events.head == MAX_EVENTS) | |
vid_mouse_events.head = 0; | |
SDL_SemPost (vid_mouse_events.sem); | |
return SCPE_OK; | |
} | |
SDL_SemPost (vid_mouse_events.sem); | |
} | |
return SCPE_EOF; | |
} | |
void vid_draw (int32 x, int32 y, int32 w, int32 h, uint32 *buf) | |
{ | |
int32 i; | |
uint32* pixels; | |
pixels = (uint32 *)vid_image->pixels; | |
for (i = y; i < (y + h); i++) | |
memcpy (pixels + (i * vid_width) + x, buf, (size_t)w*sizeof(*pixels)); | |
} | |
void vid_refresh (void) | |
{ | |
SDL_Event user_event; | |
user_event.type = SDL_USEREVENT; | |
user_event.user.code = EVENT_REDRAW; | |
user_event.user.data1 = NULL; | |
user_event.user.data2 = NULL; | |
SDL_PushEvent (&user_event); | |
} | |
int vid_map_key (int key) | |
{ | |
switch (key) { | |
case SDLK_BACKSPACE: | |
return SIM_KEY_BACKSPACE; | |
case SDLK_TAB: | |
return SIM_KEY_TAB; | |
case SDLK_RETURN: | |
return SIM_KEY_ENTER; | |
case SDLK_PAUSE: | |
return SIM_KEY_PAUSE; | |
case SDLK_ESCAPE: | |
return SIM_KEY_ESC; | |
case SDLK_SPACE: | |
return SIM_KEY_SPACE; | |
case SDLK_QUOTE: | |
return SIM_KEY_SINGLE_QUOTE; | |
case SDLK_COMMA: | |
return SIM_KEY_COMMA; | |
case SDLK_MINUS: | |
return SIM_KEY_MINUS; | |
case SDLK_PERIOD: | |
return SIM_KEY_PERIOD; | |
case SDLK_SLASH: | |
return SIM_KEY_SLASH; | |
case SDLK_0: | |
return SIM_KEY_0; | |
case SDLK_1: | |
return SIM_KEY_1; | |
case SDLK_2: | |
return SIM_KEY_2; | |
case SDLK_3: | |
return SIM_KEY_3; | |
case SDLK_4: | |
return SIM_KEY_4; | |
case SDLK_5: | |
return SIM_KEY_5; | |
case SDLK_6: | |
return SIM_KEY_6; | |
case SDLK_7: | |
return SIM_KEY_7; | |
case SDLK_8: | |
return SIM_KEY_8; | |
case SDLK_9: | |
return SIM_KEY_9; | |
case SDLK_SEMICOLON: | |
return SIM_KEY_SEMICOLON; | |
case SDLK_EQUALS: | |
return SIM_KEY_EQUALS; | |
case SDLK_LEFTBRACKET: | |
return SIM_KEY_LEFT_BRACKET; | |
case SDLK_BACKSLASH: | |
return SIM_KEY_BACKSLASH; | |
case SDLK_RIGHTBRACKET: | |
return SIM_KEY_RIGHT_BRACKET; | |
case SDLK_BACKQUOTE: | |
return SIM_KEY_BACKQUOTE; | |
case SDLK_a: | |
return SIM_KEY_A; | |
case SDLK_b: | |
return SIM_KEY_B; | |
case SDLK_c: | |
return SIM_KEY_C; | |
case SDLK_d: | |
return SIM_KEY_D; | |
case SDLK_e: | |
return SIM_KEY_E; | |
case SDLK_f: | |
return SIM_KEY_F; | |
case SDLK_g: | |
return SIM_KEY_G; | |
case SDLK_h: | |
return SIM_KEY_H; | |
case SDLK_i: | |
return SIM_KEY_I; | |
case SDLK_j: | |
return SIM_KEY_J; | |
case SDLK_k: | |
return SIM_KEY_K; | |
case SDLK_l: | |
return SIM_KEY_L; | |
case SDLK_m: | |
return SIM_KEY_M; | |
case SDLK_n: | |
return SIM_KEY_N; | |
case SDLK_o: | |
return SIM_KEY_O; | |
case SDLK_p: | |
return SIM_KEY_P; | |
case SDLK_q: | |
return SIM_KEY_Q; | |
case SDLK_r: | |
return SIM_KEY_R; | |
case SDLK_s: | |
return SIM_KEY_S; | |
case SDLK_t: | |
return SIM_KEY_T; | |
case SDLK_u: | |
return SIM_KEY_U; | |
case SDLK_v: | |
return SIM_KEY_V; | |
case SDLK_w: | |
return SIM_KEY_W; | |
case SDLK_x: | |
return SIM_KEY_X; | |
case SDLK_y: | |
return SIM_KEY_Y; | |
case SDLK_z: | |
return SIM_KEY_Z; | |
case SDLK_DELETE: | |
return SIM_KEY_DELETE; | |
case SDLK_KP0: | |
return SIM_KEY_KP_INSERT; | |
case SDLK_KP1: | |
return SIM_KEY_KP_END; | |
case SDLK_KP2: | |
return SIM_KEY_KP_DOWN; | |
case SDLK_KP3: | |
return SIM_KEY_KP_PAGE_DOWN; | |
case SDLK_KP4: | |
return SIM_KEY_KP_LEFT; | |
case SDLK_KP5: | |
return SIM_KEY_KP_5; | |
case SDLK_KP6: | |
return SIM_KEY_KP_RIGHT; | |
case SDLK_KP7: | |
return SIM_KEY_KP_HOME; | |
case SDLK_KP8: | |
return SIM_KEY_KP_UP; | |
case SDLK_KP9: | |
return SIM_KEY_KP_PAGE_UP; | |
case SDLK_KP_PERIOD: | |
return SIM_KEY_KP_DELETE; | |
case SDLK_KP_DIVIDE: | |
return SIM_KEY_KP_DIVIDE; | |
case SDLK_KP_MULTIPLY: | |
return SIM_KEY_KP_MULTIPLY; | |
case SDLK_KP_MINUS: | |
return SIM_KEY_KP_SUBTRACT; | |
case SDLK_KP_PLUS: | |
return SIM_KEY_KP_ADD; | |
case SDLK_KP_ENTER: | |
return SIM_KEY_KP_ENTER; | |
case SDLK_UP: | |
return SIM_KEY_UP; | |
case SDLK_DOWN: | |
return SIM_KEY_DOWN; | |
case SDLK_RIGHT: | |
return SIM_KEY_RIGHT; | |
case SDLK_LEFT: | |
return SIM_KEY_LEFT; | |
case SDLK_INSERT: | |
return SIM_KEY_INSERT; | |
case SDLK_HOME: | |
return SIM_KEY_HOME; | |
case SDLK_END: | |
return SIM_KEY_END; | |
case SDLK_PAGEUP: | |
return SIM_KEY_PAGE_UP; | |
case SDLK_PAGEDOWN: | |
return SIM_KEY_PAGE_DOWN; | |
case SDLK_F1: | |
return SIM_KEY_F1; | |
case SDLK_F2: | |
return SIM_KEY_F2; | |
case SDLK_F3: | |
return SIM_KEY_F3; | |
case SDLK_F4: | |
return SIM_KEY_F4; | |
case SDLK_F5: | |
return SIM_KEY_F5; | |
case SDLK_F6: | |
return SIM_KEY_F6; | |
case SDLK_F7: | |
return SIM_KEY_F7; | |
case SDLK_F8: | |
return SIM_KEY_F8; | |
case SDLK_F9: | |
return SIM_KEY_F9; | |
case SDLK_F10: | |
return SIM_KEY_F10; | |
case SDLK_F11: | |
return SIM_KEY_F11; | |
case SDLK_F12: | |
return SIM_KEY_F12; | |
case SDLK_NUMLOCK: | |
return SIM_KEY_NUM_LOCK; | |
case SDLK_CAPSLOCK: | |
return SIM_KEY_CAPS_LOCK; | |
case SDLK_SCROLLOCK: | |
return SIM_KEY_SCRL_LOCK; | |
case SDLK_RSHIFT: | |
return SIM_KEY_SHIFT_R; | |
case SDLK_LSHIFT: | |
return SIM_KEY_SHIFT_L; | |
case SDLK_RCTRL: | |
return SIM_KEY_CTRL_R; | |
case SDLK_LCTRL: | |
return SIM_KEY_CTRL_L; | |
case SDLK_RALT: | |
return SIM_KEY_ALT_R; | |
case SDLK_LALT: | |
return SIM_KEY_ALT_L; | |
case SDLK_RMETA: | |
return SIM_KEY_ALT_R; | |
case SDLK_LMETA: | |
return SIM_KEY_WIN_L; | |
case SDLK_LSUPER: | |
return SIM_KEY_WIN_L; | |
case SDLK_RSUPER: | |
return SIM_KEY_WIN_R; | |
case SDLK_PRINT: | |
return SIM_KEY_PRINT; | |
case SDLK_BREAK: | |
return SIM_KEY_PAUSE; | |
case SDLK_MENU: | |
return SIM_KEY_MENU; | |
default: | |
return SIM_KEY_UNKNOWN; | |
} | |
} | |
void vid_key (SDL_KeyboardEvent *event) | |
{ | |
SIM_KEY_EVENT ev; | |
if (vid_mouse_captured) { | |
static Uint8 *KeyStates = NULL; | |
static int numkeys; | |
if (!KeyStates) | |
KeyStates = SDL_GetKeyState(&numkeys); | |
if ((event->state == SDL_PRESSED) && KeyStates[SDLK_RSHIFT] && (KeyStates[SDLK_LCTRL] || KeyStates[SDLK_RCTRL])) { | |
sim_debug (SIM_VID_DBG_KEY, vid_dev, "vid_key() - Cursor Release\n"); | |
SDL_WM_GrabInput (SDL_GRAB_OFF); /* relese cursor */ | |
SDL_ShowCursor (SDL_ENABLE); /* show cursor */ | |
vid_mouse_captured = FALSE; | |
return; | |
} | |
} | |
if (!sim_is_running) | |
return; | |
if (SDL_SemWait (vid_key_events.sem) == 0) { | |
if (vid_key_events.count < MAX_EVENTS) { | |
sim_debug (SIM_VID_DBG_KEY, vid_dev, "Keyboard Event: State: %d, Keysym: %d\n", event->state, event->keysym); | |
if (event->state == SDL_PRESSED) { | |
if (!vid_key_state[event->keysym.sym]) { /* Key was not down before */ | |
vid_key_state[event->keysym.sym] = TRUE; | |
ev.key = vid_map_key (event->keysym.sym); | |
ev.state = SIM_KEYPRESS_DOWN; | |
} | |
else { | |
ev.key = vid_map_key (event->keysym.sym); | |
ev.state = SIM_KEYPRESS_REPEAT; | |
} | |
} | |
else { | |
vid_key_state[event->keysym.sym] = FALSE; | |
ev.key = vid_map_key (event->keysym.sym); | |
ev.state = SIM_KEYPRESS_UP; | |
} | |
vid_key_events.events[vid_key_events.tail++] = ev; | |
vid_key_events.count++; | |
if (vid_key_events.tail == MAX_EVENTS) | |
vid_key_events.tail = 0; | |
} | |
else { | |
sim_debug (SIM_VID_DBG_KEY, vid_dev, "Keyboard Event DISCARDED: State: %d, Keysym: %d\n", event->state, event->keysym); | |
} | |
SDL_SemPost (vid_key_events.sem); | |
} | |
} | |
void vid_mouse_move (SDL_MouseMotionEvent *event) | |
{ | |
SDL_Event dummy_event; | |
SIM_MOUSE_EVENT ev; | |
if (!vid_mouse_captured) | |
return; | |
if ((event->x == 0) || | |
(event->y == 0) || | |
(event->x == (vid_width - 1)) || | |
(event->y == (vid_height - 1))) { /* reached edge of window? */ | |
SDL_PumpEvents (); | |
while (SDL_PeepEvents (&dummy_event, 1, SDL_GETEVENT, SDL_MOUSEMOTIONMASK)) {}; | |
SDL_WarpMouse (vid_cursor_x, vid_cursor_y); /* sync position */ | |
} | |
if (!sim_is_running) | |
return; | |
vid_mouse_xrel += event->xrel; /* update cumulative x rel */ | |
vid_mouse_yrel -= event->yrel; /* update cumulative y rel */ | |
vid_mouse_b1 = (event->state & SDL_BUTTON(SDL_BUTTON_LEFT)) ? TRUE : FALSE; | |
vid_mouse_b2 = (event->state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) ? TRUE : FALSE; | |
vid_mouse_b3 = (event->state & SDL_BUTTON(SDL_BUTTON_RIGHT)) ? TRUE : FALSE; | |
if (SDL_SemWait (vid_mouse_events.sem) == 0) { | |
sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Move Event: (%d,%d)\n", event->xrel, event->yrel); | |
if (vid_mouse_events.count < MAX_EVENTS) { | |
ev.x_rel = event->xrel; | |
ev.y_rel = (-event->yrel); | |
ev.b1_state = vid_mouse_b1; | |
ev.b2_state = vid_mouse_b2; | |
ev.b3_state = vid_mouse_b3; | |
vid_mouse_events.events[vid_mouse_events.tail++] = ev; | |
vid_mouse_events.count++; | |
if (vid_mouse_events.tail == MAX_EVENTS) | |
vid_mouse_events.tail = 0; | |
} | |
SDL_SemPost (vid_mouse_events.sem); | |
} | |
} | |
void vid_mouse_button (SDL_MouseButtonEvent *event) | |
{ | |
SDL_Event dummy_event; | |
SIM_MOUSE_EVENT ev; | |
t_bool state; | |
if (!vid_mouse_captured) { | |
if ((event->state == SDL_PRESSED) && | |
(event->button == SDL_BUTTON_LEFT)) { /* left click and cursor not captured? */ | |
sim_debug (SIM_VID_DBG_KEY, vid_dev, "vid_mouse_button() - Cursor Captured\n"); | |
SDL_WM_GrabInput (SDL_GRAB_ON); /* lock cursor to window */ | |
SDL_ShowCursor (SDL_DISABLE); /* hide cursor */ | |
SDL_PumpEvents (); | |
while (SDL_PeepEvents (&dummy_event, 1, SDL_GETEVENT, SDL_MOUSEMOTIONMASK)) {}; | |
SDL_WarpMouse (vid_cursor_x, vid_cursor_y); /* sync position */ | |
vid_mouse_captured = TRUE; | |
} | |
return; | |
} | |
if (!sim_is_running) | |
return; | |
state = (event->state == SDL_PRESSED) ? TRUE : FALSE; | |
switch (event->button) { | |
case SDL_BUTTON_LEFT: | |
vid_mouse_b1 = state; | |
break; | |
case SDL_BUTTON_MIDDLE: | |
vid_mouse_b2 = state; | |
break; | |
case SDL_BUTTON_RIGHT: | |
vid_mouse_b3 = state; | |
break; | |
} | |
if (SDL_SemWait (vid_mouse_events.sem) == 0) { | |
sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Button Event: State: %d, Button: %d, (%d,%d)\n", event->state, event->button, event->x, event->y); | |
if (vid_mouse_events.count < MAX_EVENTS) { | |
ev.x_rel = 0; | |
ev.y_rel = 0; | |
ev.b1_state = vid_mouse_b1; | |
ev.b2_state = vid_mouse_b2; | |
ev.b3_state = vid_mouse_b3; | |
vid_mouse_events.events[vid_mouse_events.tail++] = ev; | |
vid_mouse_events.count++; | |
if (vid_mouse_events.tail == MAX_EVENTS) | |
vid_mouse_events.tail = 0; | |
} | |
SDL_SemPost (vid_mouse_events.sem); | |
} | |
} | |
void vid_update (void) | |
{ | |
SDL_Rect vid_dst; | |
vid_dst.x = 0; | |
vid_dst.y = 0; | |
vid_dst.w = vid_width; | |
vid_dst.h = vid_height; | |
sim_debug (SIM_VID_DBG_VIDEO, vid_dev, "Video Update Event: \n"); | |
SDL_BlitSurface (vid_image, NULL, vid_window, &vid_dst); | |
SDL_UpdateRects (vid_window, 1, &vid_dst); | |
} | |
int vid_thread (void* arg) | |
{ | |
SDL_Event event; | |
static char *eventtypes[] = { | |
"NOEVENT", /**< Unused (do not remove) */ | |
"ACTIVEEVENT", /**< Application loses/gains visibility */ | |
"KEYDOWN", /**< Keys pressed */ | |
"KEYUP", /**< Keys released */ | |
"MOUSEMOTION", /**< Mouse moved */ | |
"MOUSEBUTTONDOWN", /**< Mouse button pressed */ | |
"MOUSEBUTTONUP", /**< Mouse button released */ | |
"JOYAXISMOTION", /**< Joystick axis motion */ | |
"JOYBALLMOTION", /**< Joystick trackball motion */ | |
"JOYHATMOTION", /**< Joystick hat position change */ | |
"JOYBUTTONDOWN", /**< Joystick button pressed */ | |
"JOYBUTTONUP", /**< Joystick button released */ | |
"QUIT", /**< User-requested quit */ | |
"SYSWMEVENT", /**< System specific event */ | |
"EVENT_RESERVEDA", /**< Reserved for future use.. */ | |
"EVENT_RESERVEDB", /**< Reserved for future use.. */ | |
"VIDEORESIZE", /**< User resized video mode */ | |
"VIDEOEXPOSE", /**< Screen needs to be redrawn */ | |
"EVENT_RESERVED2", /**< Reserved for future use.. */ | |
"EVENT_RESERVED3", /**< Reserved for future use.. */ | |
"EVENT_RESERVED4", /**< Reserved for future use.. */ | |
"EVENT_RESERVED5", /**< Reserved for future use.. */ | |
"EVENT_RESERVED6", /**< Reserved for future use.. */ | |
"EVENT_RESERVED7", /**< Reserved for future use.. */ | |
"USEREVENT", /** Events SDL_USEREVENT(24) through SDL_MAXEVENTS-1(31) are for your use */ | |
"", | |
"", | |
"", | |
"", | |
"", | |
"", | |
"" | |
}; | |
sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE, vid_dev, "vid_thread() - Starting\n"); | |
SDL_Init (SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE); | |
vid_window = SDL_SetVideoMode (vid_width, vid_height, 8, 0); | |
SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); | |
if (sim_end) | |
vid_image = SDL_CreateRGBSurface (SDL_SWSURFACE, vid_width, vid_height, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); | |
else | |
vid_image = SDL_CreateRGBSurface (SDL_SWSURFACE, vid_width, vid_height, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); | |
vid_mono_palette[0] = sim_end ? 0xFF000000 : 0x000000FF; | |
vid_mono_palette[1] = 0xFFFFFFFF; | |
SDL_WM_SetCaption (&sim_name[0], &sim_name[0]); | |
memset (&vid_key_state, 0, sizeof(vid_key_state)); | |
sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE, vid_dev, "vid_thread() - Started\n"); | |
while (vid_active) { | |
if (SDL_WaitEvent (&event)) { | |
switch (event.type) { | |
case SDL_KEYDOWN: | |
case SDL_KEYUP: | |
vid_key ((SDL_KeyboardEvent*)&event); | |
break; | |
case SDL_MOUSEBUTTONDOWN: | |
case SDL_MOUSEBUTTONUP: | |
vid_mouse_button ((SDL_MouseButtonEvent*)&event); | |
break; | |
case SDL_MOUSEMOTION: | |
vid_mouse_move ((SDL_MouseMotionEvent*)&event); | |
break; | |
case SDL_USEREVENT: | |
if (event.user.code == EVENT_REDRAW) | |
vid_update (); | |
break; | |
default: | |
sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE, vid_dev, "vid_thread() - Ignored Event: Type: %s(%d)\n", eventtypes[event.type], event.type); | |
break; | |
} | |
} | |
} | |
SDL_Quit (); | |
sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE, vid_dev, "vid_thread() - Exiting\n"); | |
return 0; | |
} | |
#else /* libSDL2 implementation */ | |
t_bool vid_key_state[SDL_NUM_SCANCODES]; | |
t_bool vid_mouse_captured; | |
int32 vid_width; | |
int32 vid_height; | |
SDL_Texture *vid_texture; /* video buffer in GPU */ | |
SDL_Window *vid_window; /* window handle */ | |
SDL_Renderer *vid_renderer; | |
SDL_Thread *vid_thread_handle; /* event thread handle */ | |
uint32 vid_mono_palette[2]; /* Monochrome Color Map */ | |
SDL_Color vid_colors[256]; | |
KEY_EVENT_QUEUE vid_key_events; /* keyboard events */ | |
MOUSE_EVENT_QUEUE vid_mouse_events; /* mouse events */ | |
DEVICE *vid_dev; | |
t_stat vid_open (DEVICE *dptr, uint32 width, uint32 height) | |
{ | |
if (!vid_active) { | |
int wait_count = 0; | |
vid_active = TRUE; | |
vid_width = width; | |
vid_height = height; | |
vid_mouse_captured = FALSE; | |
vid_mouse_xrel = 0; | |
vid_mouse_yrel = 0; | |
vid_key_events.head = 0; | |
vid_key_events.tail = 0; | |
vid_key_events.count = 0; | |
vid_key_events.sem = SDL_CreateSemaphore (1); | |
vid_mouse_events.head = 0; | |
vid_mouse_events.tail = 0; | |
vid_mouse_events.count = 0; | |
vid_mouse_events.sem = SDL_CreateSemaphore (1); | |
vid_dev = dptr; | |
vid_thread_handle = SDL_CreateThread (vid_thread, "vid-thread", NULL); | |
if (vid_thread_handle == NULL) { | |
vid_close (); | |
return SCPE_OPENERR; | |
} | |
while ((!vid_texture) && (++wait_count < 20)) | |
sim_os_ms_sleep (100); | |
if (!vid_texture) { | |
vid_close (); | |
return SCPE_OPENERR; | |
} | |
sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE, vid_dev, "vid_open() - Success\n"); | |
} | |
return SCPE_OK; | |
} | |
t_stat vid_close (void) | |
{ | |
SDL_Event user_event; | |
int status; | |
if (vid_active) { | |
vid_active = FALSE; | |
if (vid_thread_handle) { | |
sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE, vid_dev, "vid_close()\n"); | |
user_event.type = SDL_USEREVENT; | |
user_event.user.code = EVENT_CLOSE; | |
user_event.user.data1 = NULL; | |
user_event.user.data2 = NULL; | |
SDL_PushEvent (&user_event); | |
SDL_WaitThread (vid_thread_handle, &status); | |
vid_thread_handle = NULL; | |
vid_dev = NULL; | |
} | |
if (vid_mouse_events.sem) { | |
SDL_DestroySemaphore(vid_mouse_events.sem); | |
vid_mouse_events.sem = NULL; | |
} | |
if (vid_key_events.sem) { | |
SDL_DestroySemaphore(vid_key_events.sem); | |
vid_key_events.sem = NULL; | |
} | |
} | |
return SCPE_OK; | |
} | |
t_stat vid_poll_kb (SIM_KEY_EVENT *ev) | |
{ | |
if (SDL_SemTryWait (vid_key_events.sem) == 0) { /* get lock */ | |
if (vid_key_events.count > 0) { /* events in queue? */ | |
*ev = vid_key_events.events[vid_key_events.head++]; | |
vid_key_events.count--; | |
if (vid_key_events.head == MAX_EVENTS) | |
vid_key_events.head = 0; | |
SDL_SemPost (vid_key_events.sem); | |
return SCPE_OK; | |
} | |
SDL_SemPost (vid_key_events.sem); | |
} | |
return SCPE_EOF; | |
} | |
t_stat vid_poll_mouse (SIM_MOUSE_EVENT *ev) | |
{ | |
if (SDL_SemTryWait (vid_mouse_events.sem) == 0) { | |
if (vid_mouse_events.count > 0) { | |
*ev = vid_mouse_events.events[vid_mouse_events.head++]; | |
vid_mouse_events.count--; | |
if (vid_mouse_events.head == MAX_EVENTS) | |
vid_mouse_events.head = 0; | |
SDL_SemPost (vid_mouse_events.sem); | |
return SCPE_OK; | |
} | |
SDL_SemPost (vid_mouse_events.sem); | |
} | |
return SCPE_EOF; | |
} | |
void vid_draw (int32 x, int32 y, int32 w, int32 h, uint32 *buf) | |
{ | |
SDL_Rect vid_dst; | |
vid_dst.x = x; | |
vid_dst.y = y; | |
vid_dst.w = w; | |
vid_dst.h = h; | |
sim_debug (SIM_VID_DBG_VIDEO, vid_dev, "vid_draw(%d, %d, %d, %d)\n", x, y, w, h); | |
if (SDL_UpdateTexture(vid_texture, &vid_dst, buf, w*sizeof(*buf))) { | |
sim_debug (SIM_VID_DBG_VIDEO, vid_dev, "vid_draw() - SDL_UpdateTexture error: %s\n", SDL_GetError()); | |
} | |
} | |
void vid_refresh (void) | |
{ | |
SDL_Event user_event; | |
sim_debug (SIM_VID_DBG_VIDEO, vid_dev, "vid_refresh() - Queueing Refresh Event\n"); | |
user_event.type = SDL_USEREVENT; | |
user_event.user.code = EVENT_REDRAW; | |
user_event.user.data1 = NULL; | |
user_event.user.data2 = NULL; | |
if (SDL_PushEvent (&user_event) < 0) { | |
sim_debug (SIM_VID_DBG_VIDEO, vid_dev, "vid_refresh() SDL_PushEvent error: %s\n", SDL_GetError()); | |
} | |
} | |
int vid_map_key (int key) | |
{ | |
switch (key) { | |
case SDLK_BACKSPACE: | |
return SIM_KEY_BACKSPACE; | |
case SDLK_TAB: | |
return SIM_KEY_TAB; | |
case SDLK_RETURN: | |
return SIM_KEY_ENTER; | |
case SDLK_ESCAPE: | |
return SIM_KEY_ESC; | |
case SDLK_SPACE: | |
return SIM_KEY_SPACE; | |
case SDLK_QUOTE: | |
return SIM_KEY_SINGLE_QUOTE; | |
case SDLK_COMMA: | |
return SIM_KEY_COMMA; | |
case SDLK_MINUS: | |
return SIM_KEY_MINUS; | |
case SDLK_PERIOD: | |
return SIM_KEY_PERIOD; | |
case SDLK_SLASH: | |
return SIM_KEY_SLASH; | |
case SDLK_0: | |
return SIM_KEY_0; | |
case SDLK_1: | |
return SIM_KEY_1; | |
case SDLK_2: | |
return SIM_KEY_2; | |
case SDLK_3: | |
return SIM_KEY_3; | |
case SDLK_4: | |
return SIM_KEY_4; | |
case SDLK_5: | |
return SIM_KEY_5; | |
case SDLK_6: | |
return SIM_KEY_6; | |
case SDLK_7: | |
return SIM_KEY_7; | |
case SDLK_8: | |
return SIM_KEY_8; | |
case SDLK_9: | |
return SIM_KEY_9; | |
case SDLK_SEMICOLON: | |
return SIM_KEY_SEMICOLON; | |
case SDLK_EQUALS: | |
return SIM_KEY_EQUALS; | |
case SDLK_LEFTBRACKET: | |
return SIM_KEY_LEFT_BRACKET; | |
case SDLK_BACKSLASH: | |
return SIM_KEY_BACKSLASH; | |
case SDLK_RIGHTBRACKET: | |
return SIM_KEY_RIGHT_BRACKET; | |
case SDLK_BACKQUOTE: | |
return SIM_KEY_BACKQUOTE; | |
case SDLK_a: | |
return SIM_KEY_A; | |
case SDLK_b: | |
return SIM_KEY_B; | |
case SDLK_c: | |
return SIM_KEY_C; | |
case SDLK_d: | |
return SIM_KEY_D; | |
case SDLK_e: | |
return SIM_KEY_E; | |
case SDLK_f: | |
return SIM_KEY_F; | |
case SDLK_g: | |
return SIM_KEY_G; | |
case SDLK_h: | |
return SIM_KEY_H; | |
case SDLK_i: | |
return SIM_KEY_I; | |
case SDLK_j: | |
return SIM_KEY_J; | |
case SDLK_k: | |
return SIM_KEY_K; | |
case SDLK_l: | |
return SIM_KEY_L; | |
case SDLK_m: | |
return SIM_KEY_M; | |
case SDLK_n: | |
return SIM_KEY_N; | |
case SDLK_o: | |
return SIM_KEY_O; | |
case SDLK_p: | |
return SIM_KEY_P; | |
case SDLK_q: | |
return SIM_KEY_Q; | |
case SDLK_r: | |
return SIM_KEY_R; | |
case SDLK_s: | |
return SIM_KEY_S; | |
case SDLK_t: | |
return SIM_KEY_T; | |
case SDLK_u: | |
return SIM_KEY_U; | |
case SDLK_v: | |
return SIM_KEY_V; | |
case SDLK_w: | |
return SIM_KEY_W; | |
case SDLK_x: | |
return SIM_KEY_X; | |
case SDLK_y: | |
return SIM_KEY_Y; | |
case SDLK_z: | |
return SIM_KEY_Z; | |
case SDLK_DELETE: | |
return SIM_KEY_DELETE; | |
case SDLK_KP_0: | |
return SIM_KEY_KP_INSERT; | |
case SDLK_KP_1: | |
return SIM_KEY_KP_END; | |
case SDLK_KP_2: | |
return SIM_KEY_KP_DOWN; | |
case SDLK_KP_3: | |
return SIM_KEY_KP_PAGE_DOWN; | |
case SDLK_KP_4: | |
return SIM_KEY_KP_LEFT; | |
case SDLK_KP_5: | |
return SIM_KEY_KP_5; | |
case SDLK_KP_6: | |
return SIM_KEY_KP_RIGHT; | |
case SDLK_KP_7: | |
return SIM_KEY_KP_HOME; | |
case SDLK_KP_8: | |
return SIM_KEY_KP_UP; | |
case SDLK_KP_9: | |
return SIM_KEY_KP_PAGE_UP; | |
case SDLK_KP_PERIOD: | |
return SIM_KEY_KP_DELETE; | |
case SDLK_KP_DIVIDE: | |
return SIM_KEY_KP_DIVIDE; | |
case SDLK_KP_MULTIPLY: | |
return SIM_KEY_KP_MULTIPLY; | |
case SDLK_KP_MINUS: | |
return SIM_KEY_KP_SUBTRACT; | |
case SDLK_KP_PLUS: | |
return SIM_KEY_KP_ADD; | |
case SDLK_KP_ENTER: | |
return SIM_KEY_KP_ENTER; | |
case SDLK_UP: | |
return SIM_KEY_UP; | |
case SDLK_DOWN: | |
return SIM_KEY_DOWN; | |
case SDLK_RIGHT: | |
return SIM_KEY_RIGHT; | |
case SDLK_LEFT: | |
return SIM_KEY_LEFT; | |
case SDLK_INSERT: | |
return SIM_KEY_INSERT; | |
case SDLK_HOME: | |
return SIM_KEY_HOME; | |
case SDLK_END: | |
return SIM_KEY_END; | |
case SDLK_PAGEUP: | |
return SIM_KEY_PAGE_UP; | |
case SDLK_PAGEDOWN: | |
return SIM_KEY_PAGE_DOWN; | |
case SDLK_F1: | |
return SIM_KEY_F1; | |
case SDLK_F2: | |
return SIM_KEY_F2; | |
case SDLK_F3: | |
return SIM_KEY_F3; | |
case SDLK_F4: | |
return SIM_KEY_F4; | |
case SDLK_F5: | |
return SIM_KEY_F5; | |
case SDLK_F6: | |
return SIM_KEY_F6; | |
case SDLK_F7: | |
return SIM_KEY_F7; | |
case SDLK_F8: | |
return SIM_KEY_F8; | |
case SDLK_F9: | |
return SIM_KEY_F9; | |
case SDLK_F10: | |
return SIM_KEY_F10; | |
case SDLK_F11: | |
return SIM_KEY_F11; | |
case SDLK_F12: | |
return SIM_KEY_F12; | |
case SDLK_NUMLOCKCLEAR: | |
return SIM_KEY_NUM_LOCK; | |
case SDLK_CAPSLOCK: | |
return SIM_KEY_CAPS_LOCK; | |
case SDLK_SCROLLLOCK: | |
return SIM_KEY_SCRL_LOCK; | |
case SDLK_RSHIFT: | |
return SIM_KEY_SHIFT_R; | |
case SDLK_LSHIFT: | |
return SIM_KEY_SHIFT_L; | |
case SDLK_RCTRL: | |
return SIM_KEY_CTRL_R; | |
case SDLK_LCTRL: | |
return SIM_KEY_CTRL_L; | |
case SDLK_RALT: | |
return SIM_KEY_ALT_R; | |
case SDLK_LALT: | |
return SIM_KEY_ALT_L; | |
case SDLK_LGUI: | |
return SIM_KEY_WIN_L; | |
case SDLK_RGUI: | |
return SIM_KEY_WIN_R; | |
case SDLK_PRINTSCREEN: | |
return SIM_KEY_PRINT; | |
case SDLK_PAUSE: | |
return SIM_KEY_PAUSE; | |
case SDLK_MENU: | |
return SIM_KEY_MENU; | |
default: | |
return SIM_KEY_UNKNOWN; | |
} | |
} | |
void vid_key (SDL_KeyboardEvent *event) | |
{ | |
SIM_KEY_EVENT ev; | |
if (vid_mouse_captured) { | |
static const Uint8 *KeyStates = NULL; | |
static int numkeys; | |
if (!KeyStates) | |
KeyStates = SDL_GetKeyboardState(&numkeys); | |
if ((event->state == SDL_PRESSED) && KeyStates[SDL_SCANCODE_RSHIFT] && (KeyStates[SDL_SCANCODE_LCTRL] || KeyStates[SDL_SCANCODE_RCTRL])) { | |
sim_debug (SIM_VID_DBG_KEY, vid_dev, "vid_key() - Cursor Release\n"); | |
SDL_SetRelativeMouseMode(SDL_FALSE); /* release cursor, show cursor */ | |
vid_mouse_captured = FALSE; | |
return; | |
} | |
} | |
if (!sim_is_running) | |
return; | |
if (SDL_SemWait (vid_key_events.sem) == 0) { | |
sim_debug (SIM_VID_DBG_KEY, vid_dev, "Keyboard Event: State: %d, Keysym(scancode,sym): (%d,%d)\n", event->state, event->keysym.scancode, event->keysym.sym); | |
if (vid_key_events.count < MAX_EVENTS) { | |
if (event->state == SDL_PRESSED) { | |
if (!vid_key_state[event->keysym.scancode]) { /* Key was not down before */ | |
vid_key_state[event->keysym.scancode] = TRUE; | |
ev.key = vid_map_key (event->keysym.sym); | |
ev.state = SIM_KEYPRESS_DOWN; | |
} | |
else { | |
ev.key = vid_map_key (event->keysym.sym); | |
ev.state = SIM_KEYPRESS_REPEAT; | |
} | |
} | |
else { | |
vid_key_state[event->keysym.scancode] = FALSE; | |
ev.key = vid_map_key (event->keysym.sym); | |
ev.state = SIM_KEYPRESS_UP; | |
} | |
vid_key_events.events[vid_key_events.tail++] = ev; | |
vid_key_events.count++; | |
if (vid_key_events.tail == MAX_EVENTS) | |
vid_key_events.tail = 0; | |
} | |
SDL_SemPost (vid_key_events.sem); | |
} | |
} | |
void vid_mouse_move (SDL_MouseMotionEvent *event) | |
{ | |
SDL_Event dummy_event; | |
SIM_MOUSE_EVENT ev; | |
if (!vid_mouse_captured) | |
return; | |
if ((event->x == 0) || | |
(event->y == 0) || | |
(event->x == (vid_width - 1)) || | |
(event->y == (vid_height - 1))) { /* reached edge of window? */ | |
SDL_PumpEvents (); | |
while (SDL_PeepEvents (&dummy_event, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION)) {}; | |
SDL_WarpMouseInWindow (NULL, vid_cursor_x, vid_cursor_y);/* sync position */ | |
} | |
if (!sim_is_running) | |
return; | |
while (SDL_PeepEvents (&dummy_event, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION)) { | |
/* Coalesce motion activity to avoid thrashing */ | |
event->xrel += ((SDL_MouseMotionEvent *)&dummy_event)->xrel; | |
event->yrel += ((SDL_MouseMotionEvent *)&dummy_event)->yrel; | |
event->x = ((SDL_MouseMotionEvent *)&dummy_event)->x; | |
event->y = ((SDL_MouseMotionEvent *)&dummy_event)->y; | |
sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Move Event: Additional Event Coalesced:(%d,%d) rel:(%d,%d)\n", ((SDL_MouseMotionEvent *)&dummy_event)->x, ((SDL_MouseMotionEvent *)&dummy_event)->y, ((SDL_MouseMotionEvent *)&dummy_event)->xrel, ((SDL_MouseMotionEvent *)&dummy_event)->yrel, vid_mouse_events.count, vid_mouse_xrel, vid_mouse_yrel); | |
}; | |
SDL_WarpMouseInWindow (NULL, vid_cursor_x, vid_cursor_y);/* sync position */ | |
vid_mouse_xrel += event->xrel; /* update cumulative x rel */ | |
vid_mouse_yrel -= event->yrel; /* update cumulative y rel */ | |
vid_mouse_b1 = (event->state & SDL_BUTTON(SDL_BUTTON_LEFT)) ? TRUE : FALSE; | |
vid_mouse_b2 = (event->state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) ? TRUE : FALSE; | |
vid_mouse_b3 = (event->state & SDL_BUTTON(SDL_BUTTON_RIGHT)) ? TRUE : FALSE; | |
if (SDL_SemWait (vid_mouse_events.sem) == 0) { | |
sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Move Event: pos:(%d,%d) rel:(%d,%d) - Count: %d vid_mouse_rel:(%d,%d)\n", event->x, event->y, event->xrel, event->yrel, vid_mouse_events.count, vid_mouse_xrel, vid_mouse_yrel); | |
if (vid_mouse_events.count < MAX_EVENTS) { | |
ev.x_rel = event->xrel; | |
ev.y_rel = (-event->yrel); | |
ev.b1_state = vid_mouse_b1; | |
ev.b2_state = vid_mouse_b2; | |
ev.b3_state = vid_mouse_b3; | |
vid_mouse_events.events[vid_mouse_events.tail++] = ev; | |
vid_mouse_events.count++; | |
if (vid_mouse_events.tail == MAX_EVENTS) | |
vid_mouse_events.tail = 0; | |
} | |
SDL_SemPost (vid_mouse_events.sem); | |
} | |
} | |
void vid_mouse_button (SDL_MouseButtonEvent *event) | |
{ | |
SDL_Event dummy_event; | |
SIM_MOUSE_EVENT ev; | |
t_bool state; | |
if (!vid_mouse_captured) { | |
if ((event->state == SDL_PRESSED) && | |
(event->button == SDL_BUTTON_LEFT)) { /* left click and cursor not captured? */ | |
sim_debug (SIM_VID_DBG_KEY, vid_dev, "vid_mouse_button() - Cursor Captured\n"); | |
SDL_SetRelativeMouseMode(SDL_TRUE); /* lock cursor to window, hide cursor */ | |
SDL_PumpEvents (); | |
while (SDL_PeepEvents (&dummy_event, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION)) {}; | |
SDL_WarpMouseInWindow (NULL, vid_cursor_x, vid_cursor_y);/* sync position */ | |
vid_mouse_captured = TRUE; | |
} | |
return; | |
} | |
if (!sim_is_running) | |
return; | |
state = (event->state == SDL_PRESSED) ? TRUE : FALSE; | |
switch (event->button) { | |
case SDL_BUTTON_LEFT: | |
vid_mouse_b1 = state; | |
break; | |
case SDL_BUTTON_MIDDLE: | |
vid_mouse_b2 = state; | |
break; | |
case SDL_BUTTON_RIGHT: | |
vid_mouse_b3 = state; | |
break; | |
} | |
if (SDL_SemWait (vid_mouse_events.sem) == 0) { | |
sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Button Event: State: %d, Button: %d, (%d,%d)\n", event->state, event->button, event->x, event->y); | |
if (vid_mouse_events.count < MAX_EVENTS) { | |
ev.x_rel = 0; | |
ev.y_rel = 0; | |
ev.b1_state = vid_mouse_b1; | |
ev.b2_state = vid_mouse_b2; | |
ev.b3_state = vid_mouse_b3; | |
vid_mouse_events.events[vid_mouse_events.tail++] = ev; | |
vid_mouse_events.count++; | |
if (vid_mouse_events.tail == MAX_EVENTS) | |
vid_mouse_events.tail = 0; | |
} | |
if (SDL_SemPost (vid_mouse_events.sem)) { | |
sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Button Event: SDL_SemPost error: %s\n", SDL_GetError()); | |
} | |
} | |
} | |
void vid_update (void) | |
{ | |
SDL_Rect vid_dst; | |
vid_dst.x = 0; | |
vid_dst.y = 0; | |
vid_dst.w = vid_width; | |
vid_dst.h = vid_height; | |
sim_debug (SIM_VID_DBG_VIDEO, vid_dev, "Video Update Event: \n"); | |
if (sim_deb) | |
fflush (sim_deb); | |
if (SDL_RenderClear (vid_renderer)) { | |
sim_debug (SIM_VID_DBG_VIDEO, vid_dev, "Video Update Event: SDL_RenderClear error: %s\n", SDL_GetError()); | |
} | |
if (SDL_RenderCopy (vid_renderer, vid_texture, NULL, NULL)) { | |
sim_debug (SIM_VID_DBG_VIDEO, vid_dev, "Video Update Event: SDL_RenderCopy error: %s\n", SDL_GetError()); | |
} | |
SDL_RenderPresent (vid_renderer); | |
} | |
int vid_thread (void* arg) | |
{ | |
SDL_Event event; | |
static char *eventtypes[SDL_LASTEVENT]; | |
static t_bool initialized = FALSE; | |
if (!initialized) { | |
initialized = TRUE; | |
eventtypes[SDL_QUIT] = "QUIT"; /**< User-requested quit */ | |
/* These application events have special meaning on iOS, see README-ios.txt for details */ | |
eventtypes[SDL_APP_TERMINATING] = "APP_TERMINATING"; /**< The application is being terminated by the OS | |
Called on iOS in applicationWillTerminate() | |
Called on Android in onDestroy() | |
*/ | |
eventtypes[SDL_APP_LOWMEMORY] = "APP_LOWMEMORY"; /**< The application is low on memory, free memory if possible. | |
Called on iOS in applicationDidReceiveMemoryWarning() | |
Called on Android in onLowMemory() | |
*/ | |
eventtypes[SDL_APP_WILLENTERBACKGROUND] = "APP_WILLENTERBACKGROUND"; /**< The application is about to enter the background | |
Called on iOS in applicationWillResignActive() | |
Called on Android in onPause() | |
*/ | |
eventtypes[SDL_APP_DIDENTERBACKGROUND] = "APP_DIDENTERBACKGROUND"; /**< The application did enter the background and may not get CPU for some time | |
Called on iOS in applicationDidEnterBackground() | |
Called on Android in onPause() | |
*/ | |
eventtypes[SDL_APP_WILLENTERFOREGROUND] = "APP_WILLENTERFOREGROUND"; /**< The application is about to enter the foreground | |
Called on iOS in applicationWillEnterForeground() | |
Called on Android in onResume() | |
*/ | |
eventtypes[SDL_APP_DIDENTERFOREGROUND] = "APP_DIDENTERFOREGROUND"; /**< The application is now interactive | |
Called on iOS in applicationDidBecomeActive() | |
Called on Android in onResume() | |
*/ | |
/* Window events */ | |
eventtypes[SDL_WINDOWEVENT] = "WINDOWEVENT"; /**< Window state change */ | |
eventtypes[SDL_SYSWMEVENT] = "SYSWMEVENT"; /**< System specific event */ | |
/* Keyboard events */ | |
eventtypes[SDL_KEYDOWN] = "KEYDOWN"; /**< Key pressed */ | |
eventtypes[SDL_KEYUP] = "KEYUP"; /**< Key released */ | |
eventtypes[SDL_TEXTEDITING] = "TEXTEDITING"; /**< Keyboard text editing (composition) */ | |
eventtypes[SDL_TEXTINPUT] = "TEXTINPUT"; /**< Keyboard text input */ | |
/* Mouse events */ | |
eventtypes[SDL_MOUSEMOTION] = "MOUSEMOTION"; /**< Mouse moved */ | |
eventtypes[SDL_MOUSEBUTTONDOWN] = "MOUSEBUTTONDOWN"; /**< Mouse button pressed */ | |
eventtypes[SDL_MOUSEBUTTONUP] = "MOUSEBUTTONUP"; /**< Mouse button released */ | |
eventtypes[SDL_MOUSEWHEEL] = "MOUSEWHEEL"; /**< Mouse wheel motion */ | |
/* Joystick events */ | |
eventtypes[SDL_JOYAXISMOTION] = "JOYAXISMOTION"; /**< Joystick axis motion */ | |
eventtypes[SDL_JOYBALLMOTION] = "JOYBALLMOTION"; /**< Joystick trackball motion */ | |
eventtypes[SDL_JOYHATMOTION] = "JOYHATMOTION"; /**< Joystick hat position change */ | |
eventtypes[SDL_JOYBUTTONDOWN] = "JOYBUTTONDOWN"; /**< Joystick button pressed */ | |
eventtypes[SDL_JOYBUTTONUP] = "JOYBUTTONUP"; /**< Joystick button released */ | |
eventtypes[SDL_JOYDEVICEADDED] = "JOYDEVICEADDED"; /**< A new joystick has been inserted into the system */ | |
eventtypes[SDL_JOYDEVICEREMOVED] = "JOYDEVICEREMOVED"; /**< An opened joystick has been removed */ | |
/* Game controller events */ | |
eventtypes[SDL_CONTROLLERAXISMOTION] = "CONTROLLERAXISMOTION"; /**< Game controller axis motion */ | |
eventtypes[SDL_CONTROLLERBUTTONDOWN] = "CONTROLLERBUTTONDOWN"; /**< Game controller button pressed */ | |
eventtypes[SDL_CONTROLLERBUTTONUP] = "CONTROLLERBUTTONUP"; /**< Game controller button released */ | |
eventtypes[SDL_CONTROLLERDEVICEADDED] = "CONTROLLERDEVICEADDED"; /**< A new Game controller has been inserted into the system */ | |
eventtypes[SDL_CONTROLLERDEVICEREMOVED] = "CONTROLLERDEVICEREMOVED"; /**< An opened Game controller has been removed */ | |
eventtypes[SDL_CONTROLLERDEVICEREMAPPED] = "CONTROLLERDEVICEREMAPPED"; /**< The controller mapping was updated */ | |
/* Touch events */ | |
eventtypes[SDL_FINGERDOWN] = "FINGERDOWN"; | |
eventtypes[SDL_FINGERUP] = "FINGERUP"; | |
eventtypes[SDL_FINGERMOTION] = "FINGERMOTION"; | |
/* Gesture events */ | |
eventtypes[SDL_DOLLARGESTURE] = "DOLLARGESTURE"; | |
eventtypes[SDL_DOLLARRECORD] = "DOLLARRECORD"; | |
eventtypes[SDL_MULTIGESTURE] = "MULTIGESTURE"; | |
/* Clipboard events */ | |
eventtypes[SDL_CLIPBOARDUPDATE] = "CLIPBOARDUPDATE"; /**< The clipboard changed */ | |
/* Drag and drop events */ | |
eventtypes[SDL_DROPFILE] = "DROPFILE"; /**< The system requests a file open */ | |
/* Render events */ | |
eventtypes[SDL_RENDER_TARGETS_RESET] = "RENDER_TARGETS_RESET"; /**< The render targets have been reset */ | |
/** Events ::SDL_USEREVENT through ::SDL_LASTEVENT are for your use, | |
* and should be allocated with SDL_RegisterEvents() | |
*/ | |
eventtypes[SDL_USEREVENT] = "USEREVENT"; | |
} | |
sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE, vid_dev, "vid_thread() - Starting\n"); | |
vid_mono_palette[0] = sim_end ? 0xFF000000 : 0x000000FF; | |
vid_mono_palette[1] = 0xFFFFFFFF; | |
memset (&vid_key_state, 0, sizeof(vid_key_state)); | |
SDL_Init (SDL_INIT_VIDEO); | |
SDL_CreateWindowAndRenderer (vid_width, vid_height, SDL_WINDOW_SHOWN, &vid_window, &vid_renderer); | |
if ((vid_window == NULL) || (vid_renderer == NULL)) { | |
sim_printf ("%s: Error Creating Video Window: %s\b", sim_dname(vid_dev), SDL_GetError()); | |
SDL_Quit (); | |
return 0; | |
} | |
SDL_SetWindowTitle (vid_window, sim_name); | |
SDL_SetRenderDrawColor (vid_renderer, 0, 0, 0, 255); | |
SDL_RenderClear (vid_renderer); | |
SDL_RenderPresent (vid_renderer); | |
vid_texture = SDL_CreateTexture (vid_renderer, | |
SDL_PIXELFORMAT_ARGB8888, | |
SDL_TEXTUREACCESS_STREAMING, | |
vid_width, vid_height); | |
if (!vid_texture) { | |
sim_printf ("%s: Error configuring Video environment: %s\b", sim_dname(vid_dev), SDL_GetError()); | |
SDL_DestroyRenderer(vid_renderer); | |
vid_renderer = NULL; | |
SDL_DestroyWindow(vid_window); | |
vid_window = NULL; | |
SDL_Quit (); | |
return 0; | |
} | |
SDL_StopTextInput (); | |
sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE, vid_dev, "vid_thread() - Started\n"); | |
while (vid_active) { | |
int status = SDL_WaitEvent (&event); | |
if (status == 1) { | |
switch (event.type) { | |
case SDL_KEYDOWN: | |
case SDL_KEYUP: | |
vid_key ((SDL_KeyboardEvent*)&event); | |
break; | |
case SDL_MOUSEBUTTONDOWN: | |
case SDL_MOUSEBUTTONUP: | |
vid_mouse_button ((SDL_MouseButtonEvent*)&event); | |
break; | |
case SDL_MOUSEMOTION: | |
vid_mouse_move ((SDL_MouseMotionEvent*)&event); | |
break; | |
case SDL_USEREVENT: | |
/* There are 2 user events generated */ | |
/* EVENT_REDRAW to update the display */ | |
/* EVENT_CLOSE to wake up this thread and let */ | |
/* it notice vid_active has changed */ | |
if (event.user.code == EVENT_REDRAW) { | |
SDL_Event dummy_event; | |
vid_update (); | |
/* Only do a single video update between waiting for events */ | |
while (SDL_PeepEvents (&dummy_event, 1, SDL_GETEVENT, SDL_USEREVENT, SDL_USEREVENT)) { | |
sim_debug (SIM_VID_DBG_VIDEO, vid_dev, "vid_thread() - Ignored extra REDRAW Event\n"); | |
} | |
} | |
break; | |
default: | |
sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE, vid_dev, "vid_thread() - Ignored Event: Type: %s(%d)\n", eventtypes[event.type], event.type); | |
break; | |
} | |
} | |
else | |
if (status < 0) { | |
sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE, vid_dev, "vid_thread() - SDL_WaitEvent error: %s\n", SDL_GetError()); | |
} | |
} | |
SDL_DestroyTexture(vid_texture); | |
vid_texture = NULL; | |
SDL_DestroyRenderer(vid_renderer); | |
vid_renderer = NULL; | |
SDL_DestroyWindow(vid_window); | |
vid_window = NULL; | |
SDL_Quit (); | |
sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_KEY|SIM_VID_DBG_MOUSE, vid_dev, "vid_thread() - Exiting\n"); | |
return 0; | |
} | |
#endif /* libSDL2 implementation */ | |
const char *vid_version(void) | |
{ | |
static char SDLVersion[80]; | |
SDL_version compiled, running; | |
#if SDL_MAJOR_VERSION == 1 | |
const SDL_version *ver = SDL_Linked_Version(); | |
running.major = ver->major; | |
running.minor = ver->minor; | |
running.patch = ver->patch; | |
#else | |
SDL_GetVersion(&running); | |
#endif | |
SDL_VERSION(&compiled); | |
if ((compiled.major == running.major) && | |
(compiled.minor == running.minor) && | |
(compiled.patch == running.patch)) | |
sprintf(SDLVersion, "SDL Version %d.%d.%d", | |
compiled.major, compiled.minor, compiled.patch); | |
else | |
sprintf(SDLVersion, "SDL Version (Compiled: %d.%d.%d, Runtime: %d.%d.%d)", | |
compiled.major, compiled.minor, compiled.patch, | |
running.major, running.minor, running.patch); | |
return (const char *)SDLVersion; | |
} | |
t_stat vid_set_release_key (FILE* st, UNIT* uptr, int32 val, void* desc) | |
{ | |
return SCPE_NOFNC; | |
} | |
t_stat vid_show_release_key (FILE* st, UNIT* uptr, int32 val, void* desc) | |
{ | |
fprintf (st, "ReleaseKey=Ctrl-Right-Shift"); | |
return SCPE_OK; | |
} | |
#else | |
/* Non-implemented versions */ | |
uint32 vid_mono_palette[2]; /* Monochrome Color Map */ | |
t_stat vid_open (DEVICE *dptr, uint32 width, uint32 height) | |
{ | |
return SCPE_NOFNC; | |
} | |
t_stat vid_close (void) | |
{ | |
return SCPE_OK; | |
} | |
t_stat vid_poll_kb (SIM_KEY_EVENT *ev) | |
{ | |
return SCPE_EOF; | |
} | |
t_stat vid_poll_mouse (SIM_MOUSE_EVENT *ev) | |
{ | |
return SCPE_EOF; | |
} | |
void vid_draw (int32 x, int32 y, int32 w, int32 h, uint32 *buf) | |
{ | |
return; | |
} | |
void vid_refresh (void) | |
{ | |
return; | |
} | |
const char *vid_version (void) | |
{ | |
return "No Video Support"; | |
} | |
t_stat vid_set_release_key (FILE* st, UNIT* uptr, int32 val, void* desc) | |
{ | |
return SCPE_NOFNC; | |
} | |
t_stat vid_show_release_key (FILE* st, UNIT* uptr, int32 val, void* desc) | |
{ | |
fprintf (st, "no release key"); | |
return SCPE_OK; | |
} | |
#endif | |
#endif /* USE_SIM_VIDEO */ |