/* sdl2.c --- SDL2 routines used by the TV and KBD interfaces
*/
#include <signal.h>
#include <SDL.h>
#include "tv.h"
#include "kbd.h"
#include "mouse.h"
#include "utrace.h"
#include "idle.h"
#include <X11/keysym.h> // for XK_FOO meh
#include <X11/X.h> // for FOOMapIndex meh
SDL_Window *window;
SDL_Renderer *renderer;
SDL_Texture *texture;
int
sdl2_bucky(SDL_KeyboardEvent e)
{
int mod = SDL_GetModState();
if (mod & KMOD_CTRL && (e.keysym.sym == SDLK_LCTRL || e.keysym.sym == SDLK_RCTRL))
return ControlMapIndex;
if (mod & KMOD_SHIFT && (e.keysym.sym == SDLK_LSHIFT || e.keysym.sym == SDLK_RSHIFT))
return ShiftMapIndex;
if (mod & KMOD_ALT && (e.keysym.sym == SDLK_LALT || e.keysym.sym == SDLK_RALT))
return Mod1MapIndex;
// if (mod & KMOD_NUM) return Mod2MapIndex;
// if (mod & KMOD_MODE) return Mod3MapIndex;
if (mod & KMOD_GUI && (e.keysym.sym == SDLK_LGUI || e.keysym.sym == SDLK_RGUI))
return Mod4MapIndex;
// if (mod & KMOD_SCROLL) return Mod5MapIndex;
return -1;
}
int
sdl2_keysym_to_xk(SDL_KeyboardEvent e)
{
/* *INDENT-OFF* */
switch (e.keysym.sym) {
case SDLK_ESCAPE: return XK_Escape;
case SDLK_F1: return XK_F1;
case SDLK_F2: return XK_F2;
case SDLK_F3: return XK_F3;
case SDLK_F4: return XK_F4;
case SDLK_F5: return XK_F5;
case SDLK_F6: return XK_F6;
case SDLK_F7: return XK_F7;
case SDLK_PAGEUP: return XK_Page_Up;
case SDLK_PAGEDOWN: return XK_Page_Down;
case SDLK_HOME: return XK_Home;
case SDLK_END: return XK_End;
case SDLK_LEFT: return XK_Left;
case SDLK_RIGHT: return XK_Right;
case SDLK_UP: return XK_Up;
case SDLK_DOWN: return XK_Down;
case SDLK_BACKQUOTE: return e.keysym.mod & KMOD_SHIFT ? XK_asciitilde : XK_grave;
case SDLK_1: return e.keysym.mod & KMOD_SHIFT ? XK_exclam : XK_1;
case SDLK_2: return e.keysym.mod & KMOD_SHIFT ? XK_at : XK_2;
case SDLK_3: return e.keysym.mod & KMOD_SHIFT ? XK_numbersign : XK_3;
case SDLK_4: return e.keysym.mod & KMOD_SHIFT ? XK_dollar : XK_4;
case SDLK_5: return e.keysym.mod & KMOD_SHIFT ? XK_percent : XK_5;
case SDLK_6: return e.keysym.mod & KMOD_SHIFT ? XK_asciicircum : XK_6;
case SDLK_7: return e.keysym.mod & KMOD_SHIFT ? XK_ampersand : XK_7;
case SDLK_8: return e.keysym.mod & KMOD_SHIFT ? XK_asterisk : XK_8;
case SDLK_9: return e.keysym.mod & KMOD_SHIFT ? XK_parenleft : XK_9;
case SDLK_0: return e.keysym.mod & KMOD_SHIFT ? XK_parenright : XK_0;
case SDLK_MINUS: return e.keysym.mod & KMOD_SHIFT ? XK_underscore : XK_minus;
case SDLK_EQUALS: return e.keysym.mod & KMOD_SHIFT ? XK_plus : XK_equal;
case SDLK_TAB: return XK_Tab;
case SDLK_q: return e.keysym.mod & KMOD_SHIFT ? XK_Q : XK_q;
case SDLK_w: return e.keysym.mod & KMOD_SHIFT ? XK_W : XK_w;
case SDLK_e: return e.keysym.mod & KMOD_SHIFT ? XK_E : XK_e;
case SDLK_r: return e.keysym.mod & KMOD_SHIFT ? XK_R : XK_r;
case SDLK_t: return e.keysym.mod & KMOD_SHIFT ? XK_T : XK_t;
case SDLK_y: return e.keysym.mod & KMOD_SHIFT ? XK_Y : XK_y;
case SDLK_u: return e.keysym.mod & KMOD_SHIFT ? XK_U : XK_u;
case SDLK_i: return e.keysym.mod & KMOD_SHIFT ? XK_I : XK_i;
case SDLK_o: return e.keysym.mod & KMOD_SHIFT ? XK_O : XK_o;
case SDLK_p: return e.keysym.mod & KMOD_SHIFT ? XK_P : XK_p;
case SDLK_LEFTBRACKET: return e.keysym.mod & KMOD_SHIFT ? XK_braceleft : XK_bracketleft;
case SDLK_RIGHTBRACKET: return e.keysym.mod & KMOD_SHIFT ? XK_braceright : XK_bracketright;
case SDLK_BACKSLASH: return e.keysym.mod & KMOD_SHIFT ? XK_bar : XK_backslash;
case SDLK_BACKSPACE: return XK_BackSpace;
case SDLK_a: return e.keysym.mod & KMOD_SHIFT ? XK_A : XK_a;
case SDLK_s: return e.keysym.mod & KMOD_SHIFT ? XK_S : XK_s;
case SDLK_d: return e.keysym.mod & KMOD_SHIFT ? XK_D : XK_d;
case SDLK_f: return e.keysym.mod & KMOD_SHIFT ? XK_F : XK_f;
case SDLK_g: return e.keysym.mod & KMOD_SHIFT ? XK_G : XK_g;
case SDLK_h: return e.keysym.mod & KMOD_SHIFT ? XK_H : XK_h;
case SDLK_j: return e.keysym.mod & KMOD_SHIFT ? XK_J : XK_j;
case SDLK_k: return e.keysym.mod & KMOD_SHIFT ? XK_K : XK_k;
case SDLK_l: return e.keysym.mod & KMOD_SHIFT ? XK_L : XK_l;
case SDLK_SEMICOLON: return e.keysym.mod & KMOD_SHIFT ? XK_colon : XK_semicolon;
case SDLK_QUOTE: return e.keysym.mod & KMOD_SHIFT ? XK_quotedbl : XK_apostrophe;
case SDLK_RETURN: return XK_Return;
case SDLK_z: return e.keysym.mod & KMOD_SHIFT ? XK_Z : XK_z;
case SDLK_x: return e.keysym.mod & KMOD_SHIFT ? XK_X : XK_x;
case SDLK_c: return e.keysym.mod & KMOD_SHIFT ? XK_C : XK_c;
case SDLK_v: return e.keysym.mod & KMOD_SHIFT ? XK_V : XK_v;
case SDLK_b: return e.keysym.mod & KMOD_SHIFT ? XK_B : XK_b;
case SDLK_n: return e.keysym.mod & KMOD_SHIFT ? XK_N : XK_n;
case SDLK_m: return e.keysym.mod & KMOD_SHIFT ? XK_M : XK_m;
case SDLK_COMMA: return e.keysym.mod & KMOD_SHIFT ? XK_less : XK_comma;
case SDLK_PERIOD: return e.keysym.mod & KMOD_SHIFT ? XK_greater : XK_period;
case SDLK_SLASH: return e.keysym.mod & KMOD_SHIFT ? XK_question : XK_slash;
case SDLK_SPACE: return XK_space;
// case SDLK_MODE: return ???;
case SDLK_LSHIFT: return XK_Shift_L;
case SDLK_RSHIFT: return XK_Shift_R;
case SDLK_LCTRL: return XK_Control_L;
case SDLK_RCTRL: return XK_Control_R;
case SDLK_CAPSLOCK: return XK_Caps_Lock;
// case ???: return XK_Shift_Lock;
case SDLK_LGUI: return XK_Meta_L;
case SDLK_RGUI: return XK_Meta_R;
case SDLK_LALT: return XK_Alt_L;
case SDLK_RALT: return XK_Alt_R;
// case SDLK_LSUPER: return XK_Super_L;
// case SDLK_RSUPER: return XK_Super_R;
// case SDLK_LHYPER: return XK_Hyper_L;
// case SDLK_RHYPER: return XK_Hyper_R;
default: return XK_VoidSymbol;
}
/* *INDENT-ON* */
}
bool
cadet_allup_key(void)
{
bool allup;
int mods;
int shifts;
int statesize;
Uint8 *state;
allup = true;
mods = 0;
shifts = 0;
state = SDL_GetKeyboardState(&statesize);
for (int i = 0; allup && i < statesize; i++) {
int bucky;
if (state[i] != 1)
continue; /* bucky not pressed */
bucky = -1;
// This needs to be cleaned up -- translates scancode to bucky xk/index
switch (SDL_GetKeyFromScancode(i)) {
case SDLK_LSHIFT: case SDLK_RSHIFT: bucky = modifier_map[ShiftMapIndex]; break;
case SDLK_LCTRL: case SDLK_RCTRL: bucky = modifier_map[ControlMapIndex]; break;
case SDLK_LALT: case SDLK_RALT: bucky = modifier_map[Mod1MapIndex]; break;
case SDLK_LGUI: case SDLK_RGUI: bucky = modifier_map[Mod4MapIndex]; break;
// case SDLK_LSUPER: case SDLK_RSUPER:
// case SDLK_MODE: bucky = modifier_map[Mod5MapIndex]; break;
// case SDLK_MENU: bucky = modifier_map[KBD_NoSymbol]; break;
// case SDLK_COMPOSE: bucky = modifier_map[]; break;
case SDLK_CAPSLOCK:bucky = modifier_map[LockMapIndex]; break;
}
DEBUG(TRACE_KBD, "cadet_allup_key() - bucky pressed (%d), i = %d\n", bucky, i);
cadet_press_bucky(bucky, &mods, &shifts);
}
if (allup == true) {
DEBUG(TRACE_KBD, "cadet_allup_key() - all-up event; mods = 0%o, shifts = 0%o\n", mods, shifts);
cadet_shifts = shifts;
cadet_allup_event(mods);
}
return allup;
}
static void
process_key(SDL_KeyboardEvent e, int keydown)
{
// KeySym keysym;
// SDLKey keycode;
int bi;
int keysym;
idle_keyboard_activity();
keysym = sdl2_keysym_to_xk(e);
if (keysym == XK_VoidSymbol || keysym > NELEM(kbd_map)) {
NOTICE(TRACE_USIM, "cadet@sdl2): unable to translate to keysym (keysym = 0%o)\n", keysym);
return;
}
if (kbd_type == 0) {
if (!keydown)
return;
bi = 0;
int mod = SDL_GetModState();
if (mod & KMOD_SHIFT)
knight_process_bucky(ShiftMapIndex, &bi);
if (mod & KMOD_CAPS)
knight_process_bucky(LockMapIndex, &bi);
if (mod & KMOD_CTRL)
knight_process_bucky(ControlMapIndex, &bi);
// if (mod & Mod1Mask)
// knight_process_bucky(Mod1MapIndex, &bi);
// if (mod & Mod2Mask)
// knight_process_bucky(Mod2MapIndex, &bi);
// if (mod & Mod3Mask)
// knight_process_bucky(Mod3MapIndex, &bi);
// if (mod & Mod4Mask)
// knight_process_bucky(Mod4MapIndex, &bi);
// if (mod & Mod5Mask)
// knight_process_bucky(Mod5MapIndex, &bi);
knight_process_key(e.keysym.sym, bi, keydown);
} else {
bi = sdl2_bucky(e);
cadet_process_key(keysym /* xk_keysym */ , bi, keydown, &cadet_allup_key);
}
}
void
update(int u_minh, int u_minv, int hs, int vs)
{
SDL_UpdateTexture(texture, NULL, tv_bitmap, tv_width * sizeof(Uint32));
// Flush
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
}
void
sdl2_event(void)
{
SDL_Event ev;
send_accumulated_updates(&update);
kbd_dequeue_key_event();
if (is_mouse_warp) {
is_mouse_warp = 0;
SDL_WarpMouseInWindow(window, mouse_warp_x, mouse_warp_y);
}
while (SDL_PollEvent(&ev)) {
switch (ev.type) {
case SDL_WINDOWEVENT:
SDL_UpdateTexture(texture, NULL, tv_bitmap, tv_width * sizeof(Uint32));
// flush
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
break;
case SDL_KEYDOWN:
process_key(ev.key, 1);
break;
case SDL_KEYUP:
process_key(ev.key, 0);
break;
case SDL_MOUSEMOTION:
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
mouse_event(ev.button.x, ev.button.y, ev.button.button);
break;
case SDL_QUIT:
exit(0);
break;
}
}
}
float xbeep_volume = 1.0f;
int AMPLITUDE = 28000;
const int SAMPLE_RATE = 44100;
int xbeep_sample_nr = 0;
float xbeep_freq = 0.0f;
void
xbeep_audio_callback(void *user_data, Uint8 *raw_buffer, int bytes)
{
Sint16 *buffer = (Sint16 *) raw_buffer;
int length = bytes / 2; // 2 bytes per sample for AUDIO_S16SYS
float *freq = (float *) (user_data);
int amp = AMPLITUDE * xbeep_volume;
for (int i = 0; i < length; i++, xbeep_sample_nr++) {
double time = (double) xbeep_sample_nr / (double) SAMPLE_RATE;
buffer[i] = (Sint16) (amp * sin(2.0f * M_PI * (*freq) * time)); // render sine wave
}
}
void
xbeep_audio_init(void)
{
SDL_AudioSpec want;
SDL_AudioSpec have;
want.freq = SAMPLE_RATE;
want.format = AUDIO_S16SYS;
want.channels = 1;
want.samples = 2048;
want.callback = xbeep_audio_callback;
want.userdata = &xbeep_freq;
if (SDL_OpenAudio(&want, &have) != 0)
SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Failed to open audio: %s", SDL_GetError());
if (want.format != have.format)
SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Failed to get the desired AudioSpec");
SDL_PauseAudio(1);
}
void
xbeep_audio_close(void)
{
SDL_CloseAudio();
}
void
xbeep(int halfwavelength, int duration)
{
xbeep_freq = 1000000.0f / (float) (halfwavelength * 2);
xbeep_sample_nr = 0;
SDL_PauseAudio(0);
SDL_Delay(duration / 1000);
SDL_PauseAudio(1);
}
void
sdl2_beep(int v)
{
///XBEEP (MISC-INST-ENTRY %BEEP)
///;;; First argument is half-wavelength, second is duration. Both are in microseconds.
///;;; M-1 has 2nd argument (duration) which is added to initial time-check
///;;; M-2 contains most recent time check
///;;; to compute quitting time
///;;; M-C contains 1st argument, the wavelength
///;;; M-4 contains the time at which the next click must be done.
///;;; Note that the 32-bit clock wraps around once an hour, we have to be careful
///;;; to compare clock values in the correct way, namely without overflow checking.
extern uint32_t mmem[32];
#define DTP_FIX_VAL(x) ((x) & ((1 << 24) - 1))
#define DTP_FIX(x) (DTP_FIX_VAL(x) | 5 << 25)
int wavelength = DTP_FIX_VAL(mmem[7]); //M-C M-MEM 7
int duration = DTP_FIX_VAL(mmem[22]); //M-1 M-MEM 22
xbeep(wavelength, duration);
}
void
sdl2_init(void)
{
NOTICE(TRACE_USIM, "tv: using SDL2 backend for monitor and keyboard\n");
Foreground = 0xffffff; // White
Background = 0x000000; // Black
xbeep_audio_init();
SDL_CreateWindowAndRenderer(tv_width, tv_height, SDL_WINDOW_OPENGL, &window, &renderer);
SDL_ShowCursor(0); /* Invisible cursor. */
// SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
// SDL_RenderClear(renderer);
// SDL_RenderPresent(renderer);
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); // make the scaled rendering look smoother.
SDL_RenderSetLogicalSize(renderer, tv_width, tv_height);
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, tv_width, tv_height);
}