/* Copyright 2022-2026 Kurt Nienhaus
 *
 * This file is part of VgaGames.
 * VgaGames is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 * VgaGames is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with VgaGames.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include "iolib.h"
#include "input/input.h"

static const int AXIS_POSSTART = 6000;

void init_input_sdl3(void);

static VG_BOOL sdl3_input_open(void);
static void sdl3_input_close(void);
static void sdl3_input_reopen(void);
static int sdl3_input_update(void);
static const char * sdl3_input_code2name(int);
static int sdl3_input_key_uniqpressed(int);
static void sdl3_input_mouse_warp(int, int);
static void sdl3_input_mouse_grabbing(void);
static void sdl3_input_textbuffer(VG_BOOL);

static struct iolib * get_iolib(void);
static void mapkeys(struct iolib *);
static int textpos(struct vgi_input *);
static void mouse_grab(VG_BOOL);


/* set functions */
void
init_input_sdl3(void)
{
  vg4data.iolib.f.input_open = sdl3_input_open;
  vg4data.iolib.f.input_close = sdl3_input_close;
  vg4data.iolib.f.input_reopen = sdl3_input_reopen;
  vg4data.iolib.f.input_update = sdl3_input_update;
  vg4data.iolib.f.input_code2name = sdl3_input_code2name;
  vg4data.iolib.f.input_key_uniqpressed = sdl3_input_key_uniqpressed;
  vg4data.iolib.f.input_mouse_warp = sdl3_input_mouse_warp;
  vg4data.iolib.f.input_mouse_grabbing = sdl3_input_mouse_grabbing;
  vg4data.iolib.f.input_textbuffer = sdl3_input_textbuffer;
} /* Ende init_input_sdl3 */


/* check if is valid and return iolib pointer */
static struct iolib *
get_iolib(void)
{
  struct iolib *iolib = (struct iolib *)vg4data.iolib.stptr;

  if (iolib == NULL) { return NULL; }
  if (iolib->window.wd == NULL) { return NULL; }

  return iolib;
} /* Ende get_iolib */


/* sdl3_input_open:
 * open input
 * @return  VG_TRUE = OK or VG_FALSE = error
 */
static VG_BOOL
sdl3_input_open(void)
{
  struct iolib *iolib;
  int winw, winh;

  if ((iolib = get_iolib()) == NULL) { return VG_FALSE; }

  iolib->syms.SDL_SetWindowKeyboardGrab(iolib->window.wd, false);
  vg4data.lists.input.s->mouse.grab_enabled = VG_TRUE;
  mouse_grab(VG_TRUE);
  iolib->syms.SDL_GetWindowSize(iolib->window.wd, &winw, &winh);
  iolib->syms.SDL_WarpMouseInWindow(iolib->window.wd, winw / 2, winh / 2);

  iolib->input.wfocus = VG_TRUE;

  /* read mappings for gamecontrollers */
  { char buf[1024], *pt1, *pt2;
    const char *platform;
    FILE *ffg;
    size_t slen;

    platform = iolib->syms.SDL_GetPlatform();
    snprintf(buf, sizeof(buf), "%s/share/vgagames4/gamecontrollerdb.txt", vg4_get_prefix(NULL));
    if ((ffg = fopen(buf, "r")) != NULL) {
      while (fgets(buf, sizeof(buf), ffg) != NULL) {
        slen = strlen(buf);
        if (buf[slen - 1] != '\n') { outerr("gamecontrollerdb.txt: line too long"); fclose(ffg); return VG_FALSE; }
        slen--;
        if (slen > 0 && buf[slen - 1] == '\r') { slen--; }
        if (slen == 0) { continue; }
        buf[slen] = '\0';
        pt1 = strstr(buf, "platform:");
        if (pt1 == NULL) { continue; }
        pt1 += strlen("platform:");
        pt2 = strchr(pt1, ',');
        if (pt2 == NULL) { pt2 = pt1 + strlen(pt1); }
        if (strncasecmp(pt1, platform, (size_t)(pt2 - pt1)) == 0) {
          iolib->syms.SDL_AddGamepadMapping(buf);
        }
      }
      fclose(ffg);
    }
    iolib->syms.SDL_AddGamepadMapping("030000008f0e00000300000011010000,MY-POWER CO. LTD USB Gamepad,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,lefttrigger:b6,righttrigger:b7,leftstick:b10,rightstick:b11,guide:b12,leftx:a0,lefty:a1,rightx:a2,righty:a3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,");
  }

  /* open all found gamecontrollers */
  { int devnr, devmax, anzgs;
    SDL_JoystickID *jdevs;
    char bguid[64];

    anzgs = 0;
    jdevs = iolib->syms.SDL_GetJoysticks(&devmax);
    if (jdevs != NULL) {
      pinfo("%d Joysticks found\n", devmax);
      for (devnr = 0; devnr < devmax; devnr++) {
        iolib->syms.SDL_GUIDToString(iolib->syms.SDL_GetGamepadGUIDForID(jdevs[devnr]), bguid, sizeof(bguid));
        if (iolib->syms.SDL_IsGamepad(jdevs[devnr])) {
          SDL_Gamepad *gctl = iolib->syms.SDL_OpenGamepad(jdevs[devnr]);
          if (gctl != NULL) {
            const char *devname = iolib->syms.SDL_GetGamepadName(gctl);
            if (devname == NULL) { devname = "(no name)"; }
            pinfo("Device-index %d (GUID=%s): Gamecontroller found: %s\n", devnr, bguid, devname);
            /* insert gamecontroller */
            snprintf(vg4data.lists.input.s->gcs.gc[anzgs].name, sizeof(vg4data.lists.input.s->gcs.gc[anzgs].name), "%s", devname);
            iolib->input.gc[anzgs].gctl = gctl;
            iolib->input.gc[anzgs].jid = jdevs[devnr];
            vg4data.lists.input.s->gcs.max = ++anzgs;
            if (anzgs == VG_MAX_GCS) { break; }
          } else {
            pinfo("Device %d (GUID=%s): could not open gamecontroller: %s\n", devnr, bguid, iolib->syms.SDL_GetError());
          }
        } else {
          pinfo("Device %d (GUID=%s): no mapping from joystick to gamecontroller\n", devnr, bguid);
        }
      }
      iolib->syms.SDL_free(jdevs);
    }
  }

  mapkeys(iolib);

  return VG_TRUE;
} /* Ende sdl3_input_open */


/* map keys, buttons and axes */
static void
mapkeys(struct iolib *iolib)
{
  /* keys */
  iolib->input.codes.kmap = SML3_calloc(VG_INPUT_KBDCODE_MAXENUM - VG_INPUT_STARTKEY, sizeof(*iolib->input.codes.kmap));
  iolib->input.codes.kmap[VG_INPUT_NOKEY] = SDL_SCANCODE_UNKNOWN;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_ESCAPE - VG_INPUT_STARTKEY] = SDL_SCANCODE_ESCAPE;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_GRAVE - VG_INPUT_STARTKEY] = SDL_SCANCODE_GRAVE;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_1 - VG_INPUT_STARTKEY] = SDL_SCANCODE_1;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_2 - VG_INPUT_STARTKEY] = SDL_SCANCODE_2;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_3 - VG_INPUT_STARTKEY] = SDL_SCANCODE_3;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_4 - VG_INPUT_STARTKEY] = SDL_SCANCODE_4;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_5 - VG_INPUT_STARTKEY] = SDL_SCANCODE_5;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_6 - VG_INPUT_STARTKEY] = SDL_SCANCODE_6;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_7 - VG_INPUT_STARTKEY] = SDL_SCANCODE_7;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_8 - VG_INPUT_STARTKEY] = SDL_SCANCODE_8;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_9 - VG_INPUT_STARTKEY] = SDL_SCANCODE_9;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_0 - VG_INPUT_STARTKEY] = SDL_SCANCODE_0;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_MINUS - VG_INPUT_STARTKEY] = SDL_SCANCODE_MINUS;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_EQUALS - VG_INPUT_STARTKEY] = SDL_SCANCODE_EQUALS;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_BACKSPACE - VG_INPUT_STARTKEY] = SDL_SCANCODE_BACKSPACE;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_TAB - VG_INPUT_STARTKEY] = SDL_SCANCODE_TAB;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_Q - VG_INPUT_STARTKEY] = SDL_SCANCODE_Q;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_W - VG_INPUT_STARTKEY] = SDL_SCANCODE_W;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_E - VG_INPUT_STARTKEY] = SDL_SCANCODE_E;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_R - VG_INPUT_STARTKEY] = SDL_SCANCODE_R;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_T - VG_INPUT_STARTKEY] = SDL_SCANCODE_T;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_Y - VG_INPUT_STARTKEY] = SDL_SCANCODE_Y;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_U - VG_INPUT_STARTKEY] = SDL_SCANCODE_U;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_I - VG_INPUT_STARTKEY] = SDL_SCANCODE_I;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_O - VG_INPUT_STARTKEY] = SDL_SCANCODE_O;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_P - VG_INPUT_STARTKEY] = SDL_SCANCODE_P;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_LEFTBRACKET - VG_INPUT_STARTKEY] = SDL_SCANCODE_LEFTBRACKET;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_RIGHTBRACKET - VG_INPUT_STARTKEY] = SDL_SCANCODE_RIGHTBRACKET;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_BACKSLASH - VG_INPUT_STARTKEY] = SDL_SCANCODE_BACKSLASH;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_CAPSLOCK - VG_INPUT_STARTKEY] = SDL_SCANCODE_CAPSLOCK;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_A - VG_INPUT_STARTKEY] = SDL_SCANCODE_A;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_S - VG_INPUT_STARTKEY] = SDL_SCANCODE_S;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_D - VG_INPUT_STARTKEY] = SDL_SCANCODE_D;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_F - VG_INPUT_STARTKEY] = SDL_SCANCODE_F;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_G - VG_INPUT_STARTKEY] = SDL_SCANCODE_G;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_H - VG_INPUT_STARTKEY] = SDL_SCANCODE_H;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_J - VG_INPUT_STARTKEY] = SDL_SCANCODE_J;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_K - VG_INPUT_STARTKEY] = SDL_SCANCODE_K;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_L - VG_INPUT_STARTKEY] = SDL_SCANCODE_L;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_SEMICOLON - VG_INPUT_STARTKEY] = SDL_SCANCODE_SEMICOLON;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_APOSTROPHE - VG_INPUT_STARTKEY] = SDL_SCANCODE_APOSTROPHE;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_RETURN - VG_INPUT_STARTKEY] = SDL_SCANCODE_RETURN;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_LSHIFT - VG_INPUT_STARTKEY] = SDL_SCANCODE_LSHIFT;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_Z - VG_INPUT_STARTKEY] = SDL_SCANCODE_Z;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_X - VG_INPUT_STARTKEY] = SDL_SCANCODE_X;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_C - VG_INPUT_STARTKEY] = SDL_SCANCODE_C;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_V - VG_INPUT_STARTKEY] = SDL_SCANCODE_V;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_B - VG_INPUT_STARTKEY] = SDL_SCANCODE_B;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_N - VG_INPUT_STARTKEY] = SDL_SCANCODE_N;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_M - VG_INPUT_STARTKEY] = SDL_SCANCODE_M;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_COMMA - VG_INPUT_STARTKEY] = SDL_SCANCODE_COMMA;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_PERIOD - VG_INPUT_STARTKEY] = SDL_SCANCODE_PERIOD;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_SLASH - VG_INPUT_STARTKEY] = SDL_SCANCODE_SLASH;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_RSHIFT - VG_INPUT_STARTKEY] = SDL_SCANCODE_RSHIFT;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_LCTRL - VG_INPUT_STARTKEY] = SDL_SCANCODE_LCTRL;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_LGUI - VG_INPUT_STARTKEY] = SDL_SCANCODE_LGUI;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_LALT - VG_INPUT_STARTKEY] = SDL_SCANCODE_LALT;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_SPACE - VG_INPUT_STARTKEY] = SDL_SCANCODE_SPACE;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_RALT - VG_INPUT_STARTKEY] = SDL_SCANCODE_RALT;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_RGUI - VG_INPUT_STARTKEY] = SDL_SCANCODE_RGUI;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_APPLICATION - VG_INPUT_STARTKEY] = SDL_SCANCODE_APPLICATION;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_RCTRL - VG_INPUT_STARTKEY] = SDL_SCANCODE_RCTRL;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_LCURS - VG_INPUT_STARTKEY] = SDL_SCANCODE_LEFT;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_RCURS - VG_INPUT_STARTKEY] = SDL_SCANCODE_RIGHT;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_UCURS - VG_INPUT_STARTKEY] = SDL_SCANCODE_UP;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_DCURS - VG_INPUT_STARTKEY] = SDL_SCANCODE_DOWN;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_INSERT - VG_INPUT_STARTKEY] = SDL_SCANCODE_INSERT;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_HOME - VG_INPUT_STARTKEY] = SDL_SCANCODE_HOME;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_PGUP - VG_INPUT_STARTKEY] = SDL_SCANCODE_PAGEUP;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_DELETE - VG_INPUT_STARTKEY] = SDL_SCANCODE_DELETE;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_END - VG_INPUT_STARTKEY] = SDL_SCANCODE_END;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_PGDOWN - VG_INPUT_STARTKEY] = SDL_SCANCODE_PAGEDOWN;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_KP_0 - VG_INPUT_STARTKEY] = SDL_SCANCODE_KP_0;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_KP_1 - VG_INPUT_STARTKEY] = SDL_SCANCODE_KP_1;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_KP_2 - VG_INPUT_STARTKEY] = SDL_SCANCODE_KP_2;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_KP_3 - VG_INPUT_STARTKEY] = SDL_SCANCODE_KP_3;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_KP_4 - VG_INPUT_STARTKEY] = SDL_SCANCODE_KP_4;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_KP_5 - VG_INPUT_STARTKEY] = SDL_SCANCODE_KP_5;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_KP_6 - VG_INPUT_STARTKEY] = SDL_SCANCODE_KP_6;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_KP_7 - VG_INPUT_STARTKEY] = SDL_SCANCODE_KP_7;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_KP_8 - VG_INPUT_STARTKEY] = SDL_SCANCODE_KP_8;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_KP_9 - VG_INPUT_STARTKEY] = SDL_SCANCODE_KP_9;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_KP_ENTER - VG_INPUT_STARTKEY] = SDL_SCANCODE_KP_ENTER;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_F1 - VG_INPUT_STARTKEY] = SDL_SCANCODE_F1;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_F2 - VG_INPUT_STARTKEY] = SDL_SCANCODE_F2;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_F3 - VG_INPUT_STARTKEY] = SDL_SCANCODE_F3;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_F4 - VG_INPUT_STARTKEY] = SDL_SCANCODE_F4;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_F5 - VG_INPUT_STARTKEY] = SDL_SCANCODE_F5;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_F6 - VG_INPUT_STARTKEY] = SDL_SCANCODE_F6;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_F7 - VG_INPUT_STARTKEY] = SDL_SCANCODE_F7;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_F8 - VG_INPUT_STARTKEY] = SDL_SCANCODE_F8;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_F9 - VG_INPUT_STARTKEY] = SDL_SCANCODE_F9;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_F10 - VG_INPUT_STARTKEY] = SDL_SCANCODE_F10;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_F11 - VG_INPUT_STARTKEY] = SDL_SCANCODE_F11;
  iolib->input.codes.kmap[VG_INPUT_KBDCODE_F12 - VG_INPUT_STARTKEY] = SDL_SCANCODE_F12;

  /* GC: buttons */
  iolib->input.codes.bmap = SML3_calloc(VG_INPUT_GCBUTTON_MAXENUM - VG_INPUT_STARTGCBUTTON, sizeof(*iolib->input.codes.bmap));
  iolib->input.codes.bmap[VG_INPUT_NOKEY] = SDL_GAMEPAD_BUTTON_INVALID;
  iolib->input.codes.bmap[VG_INPUT_GCBUTTON_A - VG_INPUT_STARTGCBUTTON] = SDL_GAMEPAD_BUTTON_SOUTH;
  iolib->input.codes.bmap[VG_INPUT_GCBUTTON_B - VG_INPUT_STARTGCBUTTON] = SDL_GAMEPAD_BUTTON_EAST;
  iolib->input.codes.bmap[VG_INPUT_GCBUTTON_X - VG_INPUT_STARTGCBUTTON] = SDL_GAMEPAD_BUTTON_WEST;
  iolib->input.codes.bmap[VG_INPUT_GCBUTTON_Y - VG_INPUT_STARTGCBUTTON] = SDL_GAMEPAD_BUTTON_NORTH;
  iolib->input.codes.bmap[VG_INPUT_GCBUTTON_BACK - VG_INPUT_STARTGCBUTTON] = SDL_GAMEPAD_BUTTON_BACK;
  iolib->input.codes.bmap[VG_INPUT_GCBUTTON_GUIDE - VG_INPUT_STARTGCBUTTON] = SDL_GAMEPAD_BUTTON_GUIDE;
  iolib->input.codes.bmap[VG_INPUT_GCBUTTON_START - VG_INPUT_STARTGCBUTTON] = SDL_GAMEPAD_BUTTON_START;
  iolib->input.codes.bmap[VG_INPUT_GCBUTTON_LEFTSTICK - VG_INPUT_STARTGCBUTTON] = SDL_GAMEPAD_BUTTON_LEFT_STICK;
  iolib->input.codes.bmap[VG_INPUT_GCBUTTON_RIGHTSTICK - VG_INPUT_STARTGCBUTTON] = SDL_GAMEPAD_BUTTON_RIGHT_STICK;
  iolib->input.codes.bmap[VG_INPUT_GCBUTTON_LEFTSHOULDER - VG_INPUT_STARTGCBUTTON] = SDL_GAMEPAD_BUTTON_LEFT_SHOULDER;
  iolib->input.codes.bmap[VG_INPUT_GCBUTTON_RIGHTSHOULDER - VG_INPUT_STARTGCBUTTON] = SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER;
  iolib->input.codes.bmap[VG_INPUT_GCBUTTON_DPAD_UP - VG_INPUT_STARTGCBUTTON] = SDL_GAMEPAD_BUTTON_DPAD_UP;
  iolib->input.codes.bmap[VG_INPUT_GCBUTTON_DPAD_DOWN - VG_INPUT_STARTGCBUTTON] = SDL_GAMEPAD_BUTTON_DPAD_DOWN;
  iolib->input.codes.bmap[VG_INPUT_GCBUTTON_DPAD_LEFT - VG_INPUT_STARTGCBUTTON] = SDL_GAMEPAD_BUTTON_DPAD_LEFT;
  iolib->input.codes.bmap[VG_INPUT_GCBUTTON_DPAD_RIGHT - VG_INPUT_STARTGCBUTTON] = SDL_GAMEPAD_BUTTON_DPAD_RIGHT;

  /* GC: axes */
  iolib->input.codes.amap = SML3_calloc(VG_INPUT_GCAXIS_MAXENUM - VG_INPUT_STARTGCAXIS, sizeof(*iolib->input.codes.amap));
  iolib->input.codes.aval = SML3_calloc(VG_INPUT_GCAXIS_MAXENUM - VG_INPUT_STARTGCAXIS, sizeof(*iolib->input.codes.aval));
  iolib->input.codes.amap[VG_INPUT_NOKEY] = SDL_GAMEPAD_AXIS_INVALID;
  iolib->input.codes.amap[VG_INPUT_GCAXIS_LEFTX_LEFT - VG_INPUT_STARTGCAXIS] = SDL_GAMEPAD_AXIS_LEFTX;
  iolib->input.codes.aval[VG_INPUT_GCAXIS_LEFTX_LEFT - VG_INPUT_STARTGCAXIS] = -1;
  iolib->input.codes.amap[VG_INPUT_GCAXIS_LEFTX_RIGHT - VG_INPUT_STARTGCAXIS] = SDL_GAMEPAD_AXIS_LEFTX;
  iolib->input.codes.aval[VG_INPUT_GCAXIS_LEFTX_RIGHT - VG_INPUT_STARTGCAXIS] = 1;
  iolib->input.codes.amap[VG_INPUT_GCAXIS_LEFTY_UP - VG_INPUT_STARTGCAXIS] = SDL_GAMEPAD_AXIS_LEFTY;
  iolib->input.codes.aval[VG_INPUT_GCAXIS_LEFTY_UP - VG_INPUT_STARTGCAXIS] = -1;
  iolib->input.codes.amap[VG_INPUT_GCAXIS_LEFTY_DOWN - VG_INPUT_STARTGCAXIS] = SDL_GAMEPAD_AXIS_LEFTY;
  iolib->input.codes.aval[VG_INPUT_GCAXIS_LEFTY_DOWN - VG_INPUT_STARTGCAXIS] = 1;
  iolib->input.codes.amap[VG_INPUT_GCAXIS_RIGHTX_LEFT - VG_INPUT_STARTGCAXIS] = SDL_GAMEPAD_AXIS_RIGHTX;
  iolib->input.codes.aval[VG_INPUT_GCAXIS_RIGHTX_LEFT - VG_INPUT_STARTGCAXIS] = -1;
  iolib->input.codes.amap[VG_INPUT_GCAXIS_RIGHTX_RIGHT - VG_INPUT_STARTGCAXIS] = SDL_GAMEPAD_AXIS_RIGHTX;
  iolib->input.codes.aval[VG_INPUT_GCAXIS_RIGHTX_RIGHT - VG_INPUT_STARTGCAXIS] = 1;
  iolib->input.codes.amap[VG_INPUT_GCAXIS_RIGHTY_UP - VG_INPUT_STARTGCAXIS] = SDL_GAMEPAD_AXIS_RIGHTY;
  iolib->input.codes.aval[VG_INPUT_GCAXIS_RIGHTY_UP - VG_INPUT_STARTGCAXIS] = -1;
  iolib->input.codes.amap[VG_INPUT_GCAXIS_RIGHTY_DOWN - VG_INPUT_STARTGCAXIS] = SDL_GAMEPAD_AXIS_RIGHTY;
  iolib->input.codes.aval[VG_INPUT_GCAXIS_RIGHTY_DOWN - VG_INPUT_STARTGCAXIS] = 1;
  iolib->input.codes.amap[VG_INPUT_GCAXIS_TRIGGERLEFT - VG_INPUT_STARTGCAXIS] = SDL_GAMEPAD_AXIS_LEFT_TRIGGER;
  iolib->input.codes.aval[VG_INPUT_GCAXIS_TRIGGERLEFT - VG_INPUT_STARTGCAXIS] = 1;
  iolib->input.codes.amap[VG_INPUT_GCAXIS_TRIGGERRIGHT - VG_INPUT_STARTGCAXIS] = SDL_GAMEPAD_AXIS_RIGHT_TRIGGER;
  iolib->input.codes.aval[VG_INPUT_GCAXIS_TRIGGERRIGHT - VG_INPUT_STARTGCAXIS] = 1;
} /* Ende mapkeys */


/* sdl3_input_close:
 * close input
 */
static void
sdl3_input_close(void)
{
  struct iolib *iolib;
  int igc;

  if ((iolib = get_iolib()) == NULL) { return; }

  /* ungrab and show mouse */
  vg4data.lists.input.s->mouse.grabbed = VG_FALSE;
  iolib->syms.SDL_SetWindowMouseGrab(iolib->window.wd, false);
  iolib->syms.SDL_ShowCursor();

  /* close gamecontrollers and free scancodes/buttons/axes */
  for (igc = 0; igc < vg4data.lists.input.s->gcs.max; igc++) {
    iolib->syms.SDL_CloseGamepad(iolib->input.gc[igc].gctl);
    iolib->input.gc[igc].jid = 0;
    iolib->input.gc[igc].gctl = NULL;
  }

  /* free mapping */
  if (iolib->input.codes.kmap != NULL) { free(iolib->input.codes.kmap); iolib->input.codes.kmap = NULL; }
  if (iolib->input.codes.bmap != NULL) { free(iolib->input.codes.bmap); iolib->input.codes.bmap = NULL; }
  if (iolib->input.codes.amap != NULL) { free(iolib->input.codes.amap); iolib->input.codes.amap = NULL; }
  if (iolib->input.codes.aval != NULL) { free(iolib->input.codes.aval); iolib->input.codes.aval = NULL; }
} /* Ende sdl3_input_close */


/* sdl3_input_reopen:
 * reopen input (after reopening window)
 */
static void
sdl3_input_reopen(void)
{
  struct iolib *iolib;
  struct vgi_input *sptr;

  if ((iolib = get_iolib()) == NULL) { return; }

  sptr = vg4data.lists.input.s;

  mouse_grab(VG_TRUE);
  sptr->mouse.left = sptr->mouse.middle = sptr->mouse.right = 2;
} /* Ende sdl3_input_reopen */


/* sdl3_input_update:
 * update input
 * @return  0 = exit-request
 *          1 = OK
 *          2 = OK, but no focus
 */
static int
sdl3_input_update(void)
{
  struct iolib *iolib;
  SDL_Event ev;
  int ielm, igc;
  VG_BOOL mouseev;
  int aval, winw, winh, bbw, bbh;
  struct vgi_input *sptr;

  if ((iolib = get_iolib()) == NULL) { return 0; }

  vg4->window->getsize(&bbw, &bbh);

  sptr = vg4data.lists.input.s;

  /* reset temporary pressed values */
  for (ielm = 0; ielm < sptr->keys.max; ielm++) {
    if (sptr->keys.e[ielm].devs.kbd.isnew) {
      sptr->keys.e[ielm].devs.kbd.ispres = VG_FALSE;
      sptr->keys.e[ielm].devs.kbd.isnew = VG_FALSE;
    }
    for (igc = 0; igc < sptr->gcs.max; igc++) {
      if (sptr->keys.e[ielm].devs.gcs.gc[igc].isnew) {
        sptr->keys.e[ielm].devs.gcs.gc[igc].ispres = VG_FALSE;
        sptr->keys.e[ielm].devs.gcs.gc[igc].isnew = VG_FALSE;
      }
    }
  }

  mouseev = VG_FALSE;
  for (;;) {
    if (!iolib->syms.SDL_PollEvent(&ev)) { break; }

    switch (ev.type) {
      /* exit-request */
      case SDL_EVENT_QUIT:
        return 0;

      /* text-input utf8-character */
      case SDL_EVENT_TEXT_INPUT:
        if (sptr->text.tptr != NULL) {
          if (vg4_input_tbuf_allowed(ev.text.text)) {
            int tpos, nlen;
            nlen = strlen(ev.text.text);
            tpos = textpos(sptr);
            if (tpos >= 0 && tpos < (int)sptr->text.tsize - nlen) {
              memcpy(&sptr->text.tptr[tpos], ev.text.text, nlen + 1);
            }
          }
        }
        break;

      /* keyboard */
      case SDL_EVENT_KEY_DOWN:
      case SDL_EVENT_KEY_UP:
        if (!ev.key.repeat) {
          if (ev.type == SDL_EVENT_KEY_DOWN && ev.key.scancode == SDL_SCANCODE_BACKSPACE && (ev.key.mod & SDL_KMOD_CTRL)) {
            if (vg4data.lists.input.s->mouse.grab_enabled) {
              /* release and grab mouse with CTRL+Backspace */
              if (iolib->syms.SDL_GetWindowMouseGrab(iolib->window.wd)) {
                mouse_grab(VG_FALSE);
              } else {
                mouse_grab(VG_TRUE);
                iolib->syms.SDL_GetWindowSize(iolib->window.wd, &winw, &winh);
                iolib->syms.SDL_WarpMouseInWindow(iolib->window.wd, winw / 2, winh / 2);
              }
            }
          } else {
            for (ielm = sptr->keys.max - 1; ielm >= 0; ielm--) {
              if (ev.key.scancode == iolib->input.codes.kmap[sptr->keys.e[ielm].devs.kbd.code - VG_INPUT_STARTKEY]) {
                if (ev.type == SDL_EVENT_KEY_DOWN) {
                  sptr->keys.e[ielm].devs.kbd.ispres = VG_TRUE;
                  if (sptr->keys.e[ielm].pressed == 0) { sptr->keys.e[ielm].devs.kbd.isnew = VG_TRUE; }
                } else if (ev.type == SDL_EVENT_KEY_UP) {
                  sptr->keys.e[ielm].devs.kbd.ispres = VG_FALSE;
                }
                break;
              }
            }
          }
          if (vg4data.lists.input.s->mouse.grabbed && ev.type == SDL_EVENT_KEY_DOWN && ev.key.scancode == SDL_SCANCODE_BACKSPACE && sptr->text.tptr != NULL) {
            /* text-input backspace */
            int tpos = textpos(sptr);
            if (tpos > 0) {
              tpos--;
              while (--tpos >= 0) {
                if ((sptr->text.tptr[tpos] & 0xc0) != 0xc0) { break; }
              }
              sptr->text.tptr[++tpos] = '\0';
            }
          }
        }
        break;

      /* mouse */
      case SDL_EVENT_MOUSE_MOTION:
      case SDL_EVENT_MOUSE_BUTTON_DOWN:
      case SDL_EVENT_MOUSE_BUTTON_UP:
        mouseev = VG_TRUE;
        break;

      /* gamecontroller button */
      case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
      case SDL_EVENT_GAMEPAD_BUTTON_UP:
        for (igc = 0; igc < sptr->gcs.max; igc++) {
          if (ev.gbutton.which == iolib->input.gc[igc].jid) {  /* gc found */
            for (ielm = sptr->keys.max - 1; ielm >= 0; ielm--) {
              if (sptr->keys.e[ielm].devs.gcs.gc[igc].code > VG_INPUT_STARTGCBUTTON
                  && sptr->keys.e[ielm].devs.gcs.gc[igc].code < VG_INPUT_GCBUTTON_MAXENUM
                  && ev.gbutton.button == iolib->input.codes.bmap[sptr->keys.e[ielm].devs.gcs.gc[igc].code - VG_INPUT_STARTGCBUTTON]) {
                /* button found */
                if (ev.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
                  sptr->keys.e[ielm].devs.gcs.gc[igc].ispres = VG_TRUE;
                  if (sptr->keys.e[ielm].pressed == 0) { sptr->keys.e[ielm].devs.gcs.gc[igc].isnew = VG_TRUE; }
                } else if (ev.type == SDL_EVENT_GAMEPAD_BUTTON_UP) {
                  sptr->keys.e[ielm].devs.gcs.gc[igc].ispres = VG_FALSE;
                }
                break;
              }
            }
            break;
          }
        }
        break;

      /* gamecontroller axis */
      case SDL_EVENT_GAMEPAD_AXIS_MOTION:
        if (ev.gaxis.value <= -AXIS_POSSTART || ev.gaxis.value >= AXIS_POSSTART) {
          aval = (ev.gaxis.value < 0 ? -1 : 1);
        } else {
          aval = 0;
        }
        for (igc = 0; igc < sptr->gcs.max; igc++) {
          if (ev.gaxis.which == iolib->input.gc[igc].jid) {  /* gc found */
            for (ielm = sptr->keys.max - 1; ielm >= 0; ielm--) {
              if (sptr->keys.e[ielm].devs.gcs.gc[igc].code > VG_INPUT_STARTGCAXIS
                  && sptr->keys.e[ielm].devs.gcs.gc[igc].code < VG_INPUT_GCAXIS_MAXENUM
                  && ev.gaxis.axis == iolib->input.codes.amap[sptr->keys.e[ielm].devs.gcs.gc[igc].code - VG_INPUT_STARTGCAXIS]
                  && (aval == 0 || aval == iolib->input.codes.aval[sptr->keys.e[ielm].devs.gcs.gc[igc].code - VG_INPUT_STARTGCAXIS])) {
                /* axis found */
                if (aval != 0) {
                  if (!sptr->keys.e[ielm].devs.gcs.gc[igc].ispres) {
                    sptr->keys.e[ielm].devs.gcs.gc[igc].ispres = VG_TRUE;
                    if (sptr->keys.e[ielm].pressed == 0) { sptr->keys.e[ielm].devs.gcs.gc[igc].isnew = VG_TRUE; }
                  }
                } else {
                  sptr->keys.e[ielm].devs.gcs.gc[igc].ispres = VG_FALSE;
                }
                if (aval != 0) { break; }
              }
            }
            break;
          }
        }
        break;

      /* window */
      case SDL_EVENT_WINDOW_FOCUS_GAINED:
        iolib->input.wfocus = VG_TRUE;
        break;
      case SDL_EVENT_WINDOW_FOCUS_LOST:
        iolib->input.wfocus = VG_FALSE;
        break;
      case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
        { SDL_Event evtmp;
          memset(&evtmp, 0, sizeof(evtmp));
          evtmp.type = SDL_EVENT_QUIT;
          iolib->syms.SDL_PushEvent(&evtmp);
        }
        break;
      case SDL_EVENT_WINDOW_RESIZED:
        { SDL_WindowFlags wflag;
          winw = ev.window.data1;
          winh = ev.window.data2;
          wflag = iolib->syms.SDL_GetWindowFlags(iolib->window.wd);
          if (wflag & SDL_WINDOW_FULLSCREEN) {
            vg4data.lists.window.scale = VG_WINDOW_SCALE_FULL;
          } else if (wflag & SDL_WINDOW_MAXIMIZED) {
            vg4data.lists.window.scale = VG_WINDOW_SCALE_MAX;
          } else {
            if (iolib->window.bestfactor == 10) {
              if (vg4data.lists.window.scale != VG_WINDOW_SCALE_NONE && vg4data.lists.window.scale != VG_WINDOW_SCALE_BEST) {
                vg4data.lists.window.scale = VG_WINDOW_SCALE_NONE;
              }
            } else if (winw >= bbw * iolib->window.bestfactor / 10 && winh >= bbh * iolib->window.bestfactor / 10 ) {
              vg4data.lists.window.scale = VG_WINDOW_SCALE_BEST;
            } else {
              vg4data.lists.window.scale = VG_WINDOW_SCALE_NONE;
            }
          }
        }
        break;
    }
  }

  /* transfer pressed devices to press-status */
  for (ielm = 0; ielm < sptr->keys.max; ielm++) {
    aval = 1;

    /* keyboard */
    if (sptr->keys.e[ielm].devs.kbd.isnew) {
      if (sptr->keys.e[ielm].devs.kbd.ispres) {
        sptr->keys.e[ielm].devs.kbd.isnew = VG_FALSE;
      } else {
        sptr->keys.e[ielm].devs.kbd.ispres = VG_TRUE;
      }
      sptr->keys.e[ielm].pressed = 1;
      aval = 0;
    } else if (sptr->keys.e[ielm].devs.kbd.ispres) {
      aval = 0;
    }

    /* gamecontroller */
    for (igc = 0; igc < sptr->gcs.max; igc++) {
      if (sptr->keys.e[ielm].devs.gcs.gc[igc].isnew) {
        if (sptr->keys.e[ielm].devs.gcs.gc[igc].ispres) {
          sptr->keys.e[ielm].devs.gcs.gc[igc].isnew = VG_FALSE;
        } else {
          sptr->keys.e[ielm].devs.gcs.gc[igc].ispres = VG_TRUE;
        }
        sptr->keys.e[ielm].pressed = 1;
        aval = 0;
      } else if (sptr->keys.e[ielm].devs.gcs.gc[igc].ispres) {
        aval = 0;
      }
    }

    /* none pressed */
    if (aval) {
      sptr->keys.e[ielm].pressed = 0;
    }
  }

  if (mouseev) {
    float mx, my;
    SDL_MouseButtonFlags mstate = iolib->syms.SDL_GetMouseState(&mx, &my);
    sptr->mouse.x = (int)mx;
    sptr->mouse.y = (int)my;
    iolib->syms.SDL_GetWindowSize(iolib->window.wd, &winw, &winh);
    sptr->mouse.x = sptr->mouse.x * bbw / winw;
    sptr->mouse.y = sptr->mouse.y * bbh / winh;
    if (mstate & SDL_BUTTON_MASK(SDL_BUTTON_LEFT)) {
      if (!sptr->mouse.left) { sptr->mouse.left = 1; }
    } else {
      sptr->mouse.left = 0;
    }
    if (mstate & SDL_BUTTON_MASK(SDL_BUTTON_MIDDLE)) {
      if (!sptr->mouse.middle) { sptr->mouse.middle = 1; }
    } else {
      sptr->mouse.middle = 0;
    }
    if (mstate & SDL_BUTTON_MASK(SDL_BUTTON_RIGHT)) {
      if (!sptr->mouse.right) { sptr->mouse.right = 1; }
    } else {
      sptr->mouse.right = 0;
    }

    /* grab mouse if clicked into window */
    if (sptr->mouse.left && vg4data.lists.input.s->mouse.grab_enabled && !iolib->syms.SDL_GetWindowMouseGrab(iolib->window.wd)) {
      mouse_grab(VG_TRUE);
      sptr->mouse.left = sptr->mouse.middle = sptr->mouse.right = 2;
      /* iolib->syms.SDL_WarpMouseInWindow(iolib->window.wd, winw / 2, winh / 2); */
    }
  }


  if (!iolib->input.wfocus) { return 2; }
  return 1;
} /* Ende sdl3_input_update */


/* returns cursor-position in text-buffer or -1 */
static int
textpos(struct vgi_input *sptr)
{
  int ipos;

  if (sptr->text.tptr == NULL) { return -1; }

  for (ipos = 0; ipos < (int)sptr->text.tsize; ipos++) {
    if (sptr->text.tptr[ipos] == '\0') { return ipos; }
  }

  return -1;
} /* Ende textpos */


/* sdl3_input_code2name:
 * return statically name of a keyboard key according to the current keyboard layout
 * @param keycode  a code from VG_INPUT_KBDCODES
 * @return  name or empty string,
 *          (it stays valid at least until the next call to this function)
 */
static const char *
sdl3_input_code2name(int keycode)
{
  static char rbuf[8] = "";
  struct iolib *iolib;
  SDL_Scancode scod;
  int anzz;

  if ((iolib = get_iolib()) == NULL) { return ""; }
  if (keycode <= VG_INPUT_STARTKEY || keycode >= VG_INPUT_KBDCODE_MAXENUM) { return ""; }

  scod = iolib->input.codes.kmap[keycode];
  if (scod == SDL_SCANCODE_UNKNOWN) { return ""; }

  keycode = (int)iolib->syms.SDL_GetKeyFromScancode(scod, SDL_KMOD_NONE, false);
  keycode = vg4->misc->utf8_toupper(keycode);
  anzz = vg4->misc->utf8_from_codepoint(keycode, rbuf);
  rbuf[anzz] = '\0';

  return rbuf;
} /* Ende sdl3_input_code2name */


/* sdl3_input_key_uniqpressed:
 * return an uniquely pressed key
 * @param gcid  0 = keyboard or >0 = gamecontroller-ID
 * @return  VG_INPUT_NOKEY or one of VG_INPUT_KBDCODES/VG_INPUT_GCBUTTONS/VG_INPUT_GCAXES
 */
static int
sdl3_input_key_uniqpressed(int gcid)
{
  struct iolib *iolib;
  struct vgi_input *sptr;
  int ikey, rkey;

  if ((iolib = get_iolib()) == NULL) { return VG_INPUT_NOKEY; }

  sptr = vg4data.lists.input.s;
  rkey = VG_INPUT_NOKEY;

  if (gcid == 0) {  /* keyboard */
    const bool *kbdstate;
    int kbdnum;
    SDL_Scancode scod;
    kbdstate = iolib->syms.SDL_GetKeyboardState(&kbdnum);
    for (ikey = VG_INPUT_STARTKEY + 1; ikey < VG_INPUT_KBDCODE_MAXENUM; ikey++) {
      scod = iolib->input.codes.kmap[ikey - VG_INPUT_STARTKEY];
      if ((int)scod < kbdnum && kbdstate[scod]) {
        if (rkey != VG_INPUT_NOKEY) { rkey = VG_INPUT_NOKEY; goto up_end; }
        rkey = ikey;
      }
    }

  } else if (gcid <= sptr->gcs.max) {  /* gamecontroller-ID */
    Uint8 bpres;
    Sint16 apres;
    for (ikey = VG_INPUT_STARTGCBUTTON + 1; ikey < VG_INPUT_GCBUTTON_MAXENUM; ikey++) {  /* buttons */
      bpres = iolib->syms.SDL_GetGamepadButton(iolib->input.gc[gcid - 1].gctl,
                                               iolib->input.codes.bmap[ikey - VG_INPUT_STARTGCBUTTON]);
      if (bpres) {
        if (rkey != VG_INPUT_NOKEY) { rkey = VG_INPUT_NOKEY; goto up_end; }
        rkey = ikey;
      }
    }
    for (ikey = VG_INPUT_STARTGCAXIS + 1; ikey < VG_INPUT_GCAXIS_MAXENUM; ikey++) {  /* axes */
      apres = iolib->syms.SDL_GetGamepadAxis(iolib->input.gc[gcid - 1].gctl,
                                             iolib->input.codes.amap[ikey - VG_INPUT_STARTGCAXIS]);
      if (apres <= -AXIS_POSSTART || apres >= AXIS_POSSTART) {
        apres = (apres < 0 ? -1 : 1);
      } else {
        apres = 0;
      }
      if (apres != 0 && apres == iolib->input.codes.aval[ikey - VG_INPUT_STARTGCAXIS]) {
        if (rkey != VG_INPUT_NOKEY) { rkey = VG_INPUT_NOKEY; goto up_end; }
        rkey = ikey;
      }
    }
  }

up_end:
  return rkey;
} /* Ende sdl3_input_key_uniqpressed */


/* sdl3_input_mouse_warp:
 * set mouse to position in window, if grabbed
 * @param xpos  x-position
 * @param ypos  y-position
 */
static void
sdl3_input_mouse_warp(int xpos, int ypos)
{
  struct iolib *iolib;
  int winw, winh, bbw, bbh;

  if ((iolib = get_iolib()) == NULL) { return; }
  if (!vg4data.lists.input.s->mouse.grabbed) { return; }

  iolib->syms.SDL_GetWindowSize(iolib->window.wd, &winw, &winh);
  vg4->window->getsize(&bbw, &bbh);
  xpos = xpos * winw / bbw;
  ypos = ypos * winh / bbh;

  iolib->syms.SDL_WarpMouseInWindow(iolib->window.wd, xpos, ypos);
} /* Ende sdl3_input_mouse_warp */


/* sdl3_input_mouse_grabbing:
 * update mouse grabbing, must be called after changing vg4data.lists.input.s->mouse.grab_enabled
 */
static void
sdl3_input_mouse_grabbing(void)
{
  mouse_grab(VG_TRUE);
} /* Ende sdl3_input_mouse_grabbing */


/* sdl3_input_textbuffer:
 * set or unset text-input
 * @param do_on  VG_TRUE = set or VG_FALSE = unset
 */
static void
sdl3_input_textbuffer(VG_BOOL do_on)
{
  struct iolib *iolib;

  if ((iolib = get_iolib()) == NULL) { return; }

  if (do_on) {
    iolib->syms.SDL_SetEventEnabled(SDL_EVENT_TEXT_INPUT, true);
    iolib->syms.SDL_StartTextInput(iolib->window.wd);
  } else {
    iolib->syms.SDL_StopTextInput(iolib->window.wd);
    iolib->syms.SDL_SetEventEnabled(SDL_EVENT_TEXT_INPUT, false);
  }
} /* Ende sdl3_input_textbuffer */


/* (un)grab mouse */
static void
mouse_grab(VG_BOOL on)
{
  struct iolib *iolib;

  if ((iolib = get_iolib()) == NULL) { return; }

  if (vg4data.lists.input.s->mouse.grab_enabled) {
    if (on) {
      iolib->syms.SDL_ShowCursor();
      iolib->syms.SDL_HideCursor();
      iolib->syms.SDL_SetWindowMouseGrab(iolib->window.wd, true);
      vg4data.lists.input.s->mouse.grabbed = VG_TRUE;
    } else {
      vg4data.lists.input.s->mouse.grabbed = VG_FALSE;
      iolib->syms.SDL_SetWindowMouseGrab(iolib->window.wd, false);
      iolib->syms.SDL_ShowCursor();
    }
  } else {
    iolib->syms.SDL_ShowCursor();
    iolib->syms.SDL_HideCursor();
    iolib->syms.SDL_SetWindowMouseGrab(iolib->window.wd, false);
    vg4data.lists.input.s->mouse.grabbed = VG_TRUE;
  }
} /* Ende mouse_grab */
