/* sdl2.c --- SDL2 routines used by the TV and KBD interfaces */ #include <signal.h> #include <SDL.h> #include "tv.h" #include "kbd.h" #include "cadet.h" #include "knight.h" #include "mouse.h" #include "utrace.h" #include "idle.h" #include "misc.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.scancode == SDL_SCANCODE_LCTRL || e.keysym.scancode == SDL_SCANCODE_RCTRL)) return ControlMapIndex; if (mod & KMOD_SHIFT && (e.keysym.scancode == SDL_SCANCODE_LSHIFT || e.keysym.scancode == SDL_SCANCODE_RSHIFT)) return ShiftMapIndex; if (mod & KMOD_ALT && (e.keysym.scancode == SDL_SCANCODE_LALT || e.keysym.scancode == SDL_SCANCODE_RALT)) return Mod1MapIndex; // if (mod & KMOD_NUM) return Mod2MapIndex; // if (mod & KMOD_MODE) return Mod3MapIndex; if (mod & KMOD_GUI && (e.keysym.scancode == SDL_SCANCODE_LGUI || e.keysym.scancode == SDL_SCANCODE_RGUI)) return Mod4MapIndex; // if (mod & KMOD_SCROLL) return Mod5MapIndex; return -1; } int sdl2_scancode_to_xk(SDL_KeyboardEvent e) { /* *INDENT-OFF* */ switch (e.keysym.scancode) { case SDL_SCANCODE_ESCAPE: return XK_Escape; case SDL_SCANCODE_F1: return XK_F1; case SDL_SCANCODE_F2: return XK_F2; case SDL_SCANCODE_F3: return XK_F3; case SDL_SCANCODE_F4: return XK_F4; case SDL_SCANCODE_F5: return XK_F5; case SDL_SCANCODE_F6: return XK_F6; case SDL_SCANCODE_F7: return XK_F7; case SDL_SCANCODE_PAGEUP: return XK_Page_Up; case SDL_SCANCODE_PAGEDOWN: return XK_Page_Down; case SDL_SCANCODE_HOME: return XK_Home; case SDL_SCANCODE_END: return XK_End; case SDL_SCANCODE_LEFT: return XK_Left; case SDL_SCANCODE_RIGHT: return XK_Right; case SDL_SCANCODE_UP: return XK_Up; case SDL_SCANCODE_DOWN: return XK_Down; case SDL_SCANCODE_GRAVE: return e.keysym.mod & KMOD_SHIFT ? XK_asciitilde : XK_grave; case SDL_SCANCODE_1: return e.keysym.mod & KMOD_SHIFT ? XK_exclam : XK_1; case SDL_SCANCODE_2: return e.keysym.mod & KMOD_SHIFT ? XK_at : XK_2; case SDL_SCANCODE_3: return e.keysym.mod & KMOD_SHIFT ? XK_numbersign : XK_3; case SDL_SCANCODE_4: return e.keysym.mod & KMOD_SHIFT ? XK_dollar : XK_4; case SDL_SCANCODE_5: return e.keysym.mod & KMOD_SHIFT ? XK_percent : XK_5; case SDL_SCANCODE_6: return e.keysym.mod & KMOD_SHIFT ? XK_asciicircum : XK_6; case SDL_SCANCODE_7: return e.keysym.mod & KMOD_SHIFT ? XK_ampersand : XK_7; case SDL_SCANCODE_8: return e.keysym.mod & KMOD_SHIFT ? XK_asterisk : XK_8; case SDL_SCANCODE_9: return e.keysym.mod & KMOD_SHIFT ? XK_parenleft : XK_9; case SDL_SCANCODE_0: return e.keysym.mod & KMOD_SHIFT ? XK_parenright : XK_0; case SDL_SCANCODE_MINUS: return e.keysym.mod & KMOD_SHIFT ? XK_underscore : XK_minus; case SDL_SCANCODE_EQUALS: return e.keysym.mod & KMOD_SHIFT ? XK_plus : XK_equal; case SDL_SCANCODE_TAB: return XK_Tab; case SDL_SCANCODE_Q: return e.keysym.mod & KMOD_SHIFT ? XK_Q : XK_q; case SDL_SCANCODE_W: return e.keysym.mod & KMOD_SHIFT ? XK_W : XK_w; case SDL_SCANCODE_E: return e.keysym.mod & KMOD_SHIFT ? XK_E : XK_e; case SDL_SCANCODE_R: return e.keysym.mod & KMOD_SHIFT ? XK_R : XK_r; case SDL_SCANCODE_T: return e.keysym.mod & KMOD_SHIFT ? XK_T : XK_t; case SDL_SCANCODE_Y: return e.keysym.mod & KMOD_SHIFT ? XK_Y : XK_y; case SDL_SCANCODE_U: return e.keysym.mod & KMOD_SHIFT ? XK_U : XK_u; case SDL_SCANCODE_I: return e.keysym.mod & KMOD_SHIFT ? XK_I : XK_i; case SDL_SCANCODE_O: return e.keysym.mod & KMOD_SHIFT ? XK_O : XK_o; case SDL_SCANCODE_P: return e.keysym.mod & KMOD_SHIFT ? XK_P : XK_p; case SDL_SCANCODE_LEFTBRACKET: return e.keysym.mod & KMOD_SHIFT ? XK_braceleft : XK_bracketleft; case SDL_SCANCODE_RIGHTBRACKET: return e.keysym.mod & KMOD_SHIFT ? XK_braceright : XK_bracketright; case SDL_SCANCODE_BACKSLASH: return e.keysym.mod & KMOD_SHIFT ? XK_bar : XK_backslash; case SDL_SCANCODE_BACKSPACE: return XK_BackSpace; case SDL_SCANCODE_A: return e.keysym.mod & KMOD_SHIFT ? XK_A : XK_a; case SDL_SCANCODE_S: return e.keysym.mod & KMOD_SHIFT ? XK_S : XK_s; case SDL_SCANCODE_D: return e.keysym.mod & KMOD_SHIFT ? XK_D : XK_d; case SDL_SCANCODE_F: return e.keysym.mod & KMOD_SHIFT ? XK_F : XK_f; case SDL_SCANCODE_G: return e.keysym.mod & KMOD_SHIFT ? XK_G : XK_g; case SDL_SCANCODE_H: return e.keysym.mod & KMOD_SHIFT ? XK_H : XK_h; case SDL_SCANCODE_J: return e.keysym.mod & KMOD_SHIFT ? XK_J : XK_j; case SDL_SCANCODE_K: return e.keysym.mod & KMOD_SHIFT ? XK_K : XK_k; case SDL_SCANCODE_L: return e.keysym.mod & KMOD_SHIFT ? XK_L : XK_l; case SDL_SCANCODE_SEMICOLON: return e.keysym.mod & KMOD_SHIFT ? XK_colon : XK_semicolon; case SDL_SCANCODE_APOSTROPHE: return e.keysym.mod & KMOD_SHIFT ? XK_quotedbl : XK_apostrophe; case SDL_SCANCODE_RETURN: return XK_Return; case SDL_SCANCODE_Z: return e.keysym.mod & KMOD_SHIFT ? XK_Z : XK_z; case SDL_SCANCODE_X: return e.keysym.mod & KMOD_SHIFT ? XK_X : XK_x; case SDL_SCANCODE_C: return e.keysym.mod & KMOD_SHIFT ? XK_C : XK_c; case SDL_SCANCODE_V: return e.keysym.mod & KMOD_SHIFT ? XK_V : XK_v; case SDL_SCANCODE_B: return e.keysym.mod & KMOD_SHIFT ? XK_B : XK_b; case SDL_SCANCODE_N: return e.keysym.mod & KMOD_SHIFT ? XK_N : XK_n; case SDL_SCANCODE_M: return e.keysym.mod & KMOD_SHIFT ? XK_M : XK_m; case SDL_SCANCODE_COMMA: return e.keysym.mod & KMOD_SHIFT ? XK_less : XK_comma; case SDL_SCANCODE_PERIOD: return e.keysym.mod & KMOD_SHIFT ? XK_greater : XK_period; case SDL_SCANCODE_SLASH: return e.keysym.mod & KMOD_SHIFT ? XK_question : XK_slash; case SDL_SCANCODE_SPACE: return XK_space; // case SDL_SCANCODE_MODE: return ???; case SDL_SCANCODE_LSHIFT: return XK_Shift_L; case SDL_SCANCODE_RSHIFT: return XK_Shift_R; case SDL_SCANCODE_LCTRL: return XK_Control_L; case SDL_SCANCODE_RCTRL: return XK_Control_R; case SDL_SCANCODE_CAPSLOCK: return XK_Caps_Lock; // case ???: return XK_Shift_Lock; case SDL_SCANCODE_LGUI: return XK_Meta_L; case SDL_SCANCODE_RGUI: return XK_Meta_R; case SDL_SCANCODE_LALT: return XK_Alt_L; case SDL_SCANCODE_RALT: return XK_Alt_R; // case SDL_SCANCODE_LSUPER: return XK_Super_L; // case SDL_SCANCODE_RSUPER: return XK_Super_R; // case SDL_SCANCODE_LHYPER: return XK_Hyper_L; // case SDL_SCANCODE_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 SDL_SCANCODE_LSHIFT: case SDL_SCANCODE_RSHIFT: bucky = kbd_modifier_map[ShiftMapIndex]; break; case SDL_SCANCODE_LCTRL: case SDL_SCANCODE_RCTRL: bucky = kbd_modifier_map[ControlMapIndex]; break; case SDL_SCANCODE_LALT: case SDL_SCANCODE_RALT: bucky = kbd_modifier_map[Mod1MapIndex]; break; case SDL_SCANCODE_LGUI: case SDL_SCANCODE_RGUI: bucky = kbd_modifier_map[Mod4MapIndex]; break; // case SDL_SCANCODE_LSUPER: case SDL_SCANCODE_RSUPER: // case SDL_SCANCODE_MODE: bucky = kbd_modifier_map[Mod5MapIndex]; break; // case SDL_SCANCODE_MENU: bucky = kbd_modifier_map[KBD_NoSymbol]; break; // case SDL_SCANCODE_COMPOSE: bucky = kbd_modifier_map[]; break; case SDL_SCANCODE_CAPSLOCK: bucky = kbd_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 xk; idle_keyboard_activity(); xk = sdl2_scancode_to_xk(e); if (xk == XK_VoidSymbol || xk > NELEM(kbd_map)) { NOTICE(TRACE_USIM, "cadet@sdl2): unable to translate to keysym (xk = 0%o)\n", xk); 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.scancode, bi, keydown); } else { bi = sdl2_bucky(e); cadet_process_key(xk /* 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; tv_update_screen(&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; } } } void sdl2_idle_change_handler(int mode) { static int width = 22, bottom = 2, right = 2; switch (mode) { case IDLE_IDLE: // XSetForeground(display, idle_gc, tv_foreground); break; case IDLE_WORKING: // XSetForeground(display, idle_gc, tv_background); break; } // XDrawLine(display, window, idle_gc, tv_width - width - right, tv_height - bottom, tv_width - right, tv_height - bottom); } 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"); 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); register_idle_change_handler(sdl2_idle_change_handler); }