/* sdl2.c --- SDL2 routines used by the TV and KBD interfaces */ #include #include #include "tv.h" #include "kbd.h" #include "mouse.h" #include "utrace.h" #include "idle.h" #include // for XK_FOO meh #include // 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); }