diff --git a/src/keyboard.cpp b/src/keyboard.cpp index 1256e72b3c0646e343321fffd6fc91553772cdb7..e9e25f1f36604349e6db8aee9b87cc0b0cd0da37 100644 --- a/src/keyboard.cpp +++ b/src/keyboard.cpp @@ -21,11 +21,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "keyboard.h" #include "draw_helpers.h" -Keyboard::Keyboard(int pos, int targetPos, int width, int height, Config *config) +Keyboard::Keyboard(int pos, int targetPos, int width, int height, Uint32 format, Config *config) : position(static_cast<float>(pos)) , targetPosition(static_cast<float>(targetPos)) , keyboardWidth(width) , keyboardHeight(height) + , format(format) , config(config) { lastAnimTicks = SDL_GetTicks(); @@ -33,6 +34,15 @@ Keyboard::Keyboard(int pos, int targetPos, int width, int height, Config *config int Keyboard::init(SDL_Renderer *renderer) { + if (TTF_Init() == -1) { + fprintf(stderr, "ERROR: Unable to init TTF: %s\n", TTF_GetError()); + return 1; + } + font = TTF_OpenFont(config->keyboardFont.c_str(), 24); + if (!font) { + fprintf(stderr, "ERROR: Unable to load font: %s\n", TTF_GetError()); + return 1; + } loadKeymap(); int keyLong = std::strtol(config->keyRadius.c_str(), nullptr, 10); if (keyLong >= BEZIER_RESOLUTION || static_cast<double>(keyLong) > (keyboardHeight / 5.0) / 1.5) { @@ -48,11 +58,21 @@ int Keyboard::init(SDL_Renderer *renderer) fprintf(stderr, "ERROR: Unable to generate keyboard surface\n"); return 1; } - layer.texture = SDL_CreateTextureFromSurface(renderer, layer.surface); + layer.texture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_STREAMING, layer.surface->w, layer.surface->h); if (!layer.texture) { fprintf(stderr, "ERROR: Unable to generate keyboard texture\n"); return 1; } + SDL_Surface *formattedSurface = SDL_ConvertSurfaceFormat(layer.surface, format, 0); + if (!formattedSurface) { + fprintf(stderr, "ERROR: Unable to convert surface into window pixel format\n"); + return 1; + } + void *pixels; + int pitch; + SDL_LockTexture(layer.texture, nullptr, &pixels, &pitch); + memcpy(pixels, formattedSurface->pixels, formattedSurface->pitch * formattedSurface->h); + SDL_UnlockTexture(layer.texture); } lastAnimTicks = SDL_GetTicks(); return 0; @@ -147,45 +167,31 @@ bool Keyboard::isInSlideAnimation() const return (fabs(getTargetPosition() - getPosition()) > 0.001); } -void Keyboard::drawRow(SDL_Surface *surface, std::vector<touchArea> &keyVector, int x, int y, int width, int height, +void Keyboard::drawAndStoreRow(SDL_Surface *surface, std::vector<touchArea> &keyVector, int x, int y, int width, int height, const std::vector<std::string> &keys, int padding, TTF_Font *font) const { - auto keyBackground = SDL_MapRGB(surface->format, 15, 15, 15); - SDL_Color textColor = { 255, 255, 255, 0 }; - - auto background = SDL_MapRGB(surface->format, keyboardColor.r, keyboardColor.g, keyboardColor.b); int i = 0; for (const auto &keyCap : keys) { - SDL_Rect keyRect; - keyRect.x = x + (i * width) + padding; - keyRect.y = y + padding; - keyRect.w = width - (2 * padding); - keyRect.h = height - (2 * padding); - SDL_FillRect(surface, &keyRect, keyBackground); - if (keyRadius > 0) { - smooth_corners_surface(surface, background, &keyRect, keyRadius); - } - SDL_Surface *textSurface; - keyVector.push_back({ keyCap, x + (i * width), x + (i * width) + width, y, y + height }); - - textSurface = TTF_RenderUTF8_Blended(font, keyCap.c_str(), textColor); - - SDL_Rect keyCapRect; - keyCapRect.x = keyRect.x + ((keyRect.w / 2) - (textSurface->w / 2)); - keyCapRect.y = keyRect.y + ((keyRect.h / 2) - (textSurface->h / 2)); - keyCapRect.w = keyRect.w; - keyCapRect.h = keyRect.h; - SDL_BlitSurface(textSurface, nullptr, surface, &keyCapRect); - + const char *key = keyCap.c_str(); + drawAndStoreKey(surface, keyVector, x + (i * width), y, width, height, key, key, padding, font); i++; } } -void Keyboard::drawKey(SDL_Surface *surface, std::vector<touchArea> &keyVector, int x, int y, int width, int height, - char *cap, const char *key, int padding, TTF_Font *font) const +void Keyboard::drawAndStoreKey(SDL_Surface *surface, std::vector<touchArea> &keyVector, int x, int y, int width, int height, + const char *cap, const char *key, int padding, TTF_Font *font) const +{ + drawKey(surface, x, y, width, height, cap, padding, font, false); + keyVector.push_back({ key, x, x + width, y, y + height }); +} + +void Keyboard::drawKey(SDL_Surface *surface, int x, int y, int width, int height, + const char *cap, int padding, TTF_Font *font, bool selected) const { - auto keyBackground = SDL_MapRGB(surface->format, 15, 15, 15); - SDL_Color textColor = { 255, 255, 255, 0 }; + auto keyBackground = selected ? SDL_MapRGB(surface->format, 255, 255, 255) : SDL_MapRGB(surface->format, 15, 15, 15); + SDL_Color textColor = selected ? (SDL_Color) { 15, 15, 15, 0 } : (SDL_Color) { 255, 255, 255, 0 }; + + auto background = SDL_MapRGB(surface->format, keyboardColor.r, keyboardColor.g, keyboardColor.b); SDL_Rect keyRect; keyRect.x = x + padding; @@ -193,10 +199,11 @@ void Keyboard::drawKey(SDL_Surface *surface, std::vector<touchArea> &keyVector, keyRect.w = width - (2 * padding); keyRect.h = height - (2 * padding); SDL_FillRect(surface, &keyRect, keyBackground); + if (keyRadius > 0) { + smooth_corners_surface(surface, background, &keyRect, keyRadius); + } SDL_Surface *textSurface; - keyVector.push_back({ key, x, x + width, y, y + height }); - textSurface = TTF_RenderUTF8_Blended(font, cap, textColor); SDL_Rect keyCapRect; @@ -240,17 +247,6 @@ SDL_Surface *Keyboard::makeKeyboard(KeyboardLayer *layer) const int rowCount = layer->rows.size(); int rowHeight = keyboardHeight / (rowCount + 1); - if (TTF_Init() == -1) { - printf("TTF_Init: %s\n", TTF_GetError()); - return nullptr; - } - - TTF_Font *font = TTF_OpenFont(config->keyboardFont.c_str(), 24); - if (!font) { - printf("TTF_OpenFont: %s\n", TTF_GetError()); - return nullptr; - } - // Divide the bottom row in 20 columns and use that for calculations int colw = keyboardWidth / 20; @@ -264,7 +260,7 @@ SDL_Surface *Keyboard::makeKeyboard(KeyboardLayer *layer) const x = keyboardWidth / 20; if (i == 2) /* leave room for shift, "123" or "=\<" key */ x = keyboardWidth / 20 + colw * 2; - drawRow(surface, layer->keyVector, x, y, keyboardWidth / 10, + drawAndStoreRow(surface, layer->keyVector, x, y, keyboardWidth / 10, rowHeight, layer->rows[i], keyboardWidth / 100, font); y += rowHeight; i++; @@ -273,28 +269,28 @@ SDL_Surface *Keyboard::makeKeyboard(KeyboardLayer *layer) const /* Bottom-left key, 123 or ABC key based on which layer we're on: */ if (layer->layerNum < 2) { char nums[] = "123"; - drawKey(surface, layer->keyVector, colw, y, colw * 3, rowHeight, + drawAndStoreKey(surface, layer->keyVector, colw, y, colw * 3, rowHeight, nums, KEYCAP_NUMBERS, keyboardWidth / 100, font); } else { char abc[] = "abc"; - drawKey(surface, layer->keyVector, colw, y, colw * 3, rowHeight, + drawAndStoreKey(surface, layer->keyVector, colw, y, colw * 3, rowHeight, abc, KEYCAP_ABC, keyboardWidth / 100, font); } /* Shift-key that transforms into "123" or "=\<" depending on layer: */ if (layer->layerNum == 2) { char symb[] = "=\\<"; - drawKey(surface, layer->keyVector, 0, y - rowHeight, + drawAndStoreKey(surface, layer->keyVector, 0, y - rowHeight, sidebuttonsWidth, rowHeight, symb, KEYCAP_SYMBOLS, keyboardWidth / 100, font); } else if (layer->layerNum == 3) { char nums[] = "123"; - drawKey(surface, layer->keyVector, 0, y - rowHeight, + drawAndStoreKey(surface, layer->keyVector, 0, y - rowHeight, sidebuttonsWidth, rowHeight, nums, KEYCAP_NUMBERS, keyboardWidth / 100, font); } else { char shift[64] = ""; memcpy(shift, KEYCAP_SHIFT, strlen(KEYCAP_SHIFT) + 1); - drawKey(surface, layer->keyVector, 0, y - rowHeight, + drawAndStoreKey(surface, layer->keyVector, 0, y - rowHeight, sidebuttonsWidth, rowHeight, shift, KEYCAP_SHIFT, keyboardWidth / 100, font); } @@ -303,21 +299,21 @@ SDL_Surface *Keyboard::makeKeyboard(KeyboardLayer *layer) const char bcksp[64]; memcpy(bcksp, KEYCAP_BACKSPACE, strlen(KEYCAP_BACKSPACE) + 1); - drawKey(surface, layer->keyVector, keyboardWidth / 20 + colw * 16, + drawAndStoreKey(surface, layer->keyVector, keyboardWidth / 20 + colw * 16, y - rowHeight, sidebuttonsWidth, rowHeight, bcksp, KEYCAP_BACKSPACE, keyboardWidth / 100, font); } char space[] = " "; - drawKey(surface, layer->keyVector, colw * 5, y, colw * 8, rowHeight, + drawAndStoreKey(surface, layer->keyVector, colw * 5, y, colw * 8, rowHeight, space, KEYCAP_SPACE, keyboardWidth / 100, font); char period[] = "."; - drawKey(surface, layer->keyVector, colw * 13, y, colw * 2, rowHeight, + drawAndStoreKey(surface, layer->keyVector, colw * 13, y, colw * 2, rowHeight, period, KEYCAP_PERIOD, keyboardWidth / 100, font); char enter[] = "OK"; - drawKey(surface, layer->keyVector, colw * 15, y, colw * 5, rowHeight, + drawAndStoreKey(surface, layer->keyVector, colw * 15, y, colw * 5, rowHeight, enter, KEYCAP_RETURN, keyboardWidth / 100, font); return surface; @@ -378,16 +374,39 @@ void Keyboard::loadKeymap() keyboard.push_back(layer3); } -std::string Keyboard::getCharForCoordinates(int x, int y) +touchArea Keyboard::getKeyForCoordinates(int x, int y) { for (const auto &layer : keyboard) { if (layer.layerNum == activeLayer) { for (const auto &it : layer.keyVector) { if (x > it.x1 && x < it.x2 && y > it.y1 && y < it.y2) { - return it.keyChar; + return it; } } } } - return ""; + return { "", 0, 0, 0, 0 }; +} + +void Keyboard::selectKey(touchArea area, bool selected) +{ + for (const auto &layer : keyboard) { + if (layer.layerNum != activeLayer) { + continue; + } + + drawKey(layer.surface, area.x1, area.y1, area.x2 - area.x1, area.y2 - area.y1, + area.keyChar.c_str(), keyboardWidth / 100, font, selected); + + SDL_Surface *formattedSurface = SDL_ConvertSurfaceFormat(layer.surface, format, 0); + if (!formattedSurface) { + fprintf(stderr, "ERROR: Unable to convert surface into window pixel format\n"); + } + + void *pixels; + int pitch; + SDL_LockTexture(layer.texture, nullptr, &pixels, &pitch); + memcpy(pixels, formattedSurface->pixels, formattedSurface->pitch * formattedSurface->h); + SDL_UnlockTexture(layer.texture); + } } diff --git a/src/keyboard.h b/src/keyboard.h index 4e757cf94d59c1a60793c9f61e5d0d932c05b537..7aeb0314365f6d2e011af7356f6478bb42b7f2c8 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -75,16 +75,23 @@ public: @param targetPos Final position (e.g. 1 for max height) @param width Width to draw keyboard @param height Height to draw keyboard + @param format SDL_PixelFormatEnum value of the window @param config Pointer to Config */ - Keyboard(int pos, int targetPos, int width, int height, Config *config); + Keyboard(int pos, int targetPos, int width, int height, Uint32 format, Config *config); /** Get the character/key at the given coordinates @param x X-axis coordinate @param y Y-axis coordinate - @return String with value of key at the given coordinates + @return Touch area for the key at the given coordinates. When no key is found, keyChar will be an empty string. */ - std::string getCharForCoordinates(int x, int y); + touchArea getKeyForCoordinates(int x, int y); + /** + Select / unselect a specific key + @param area Touch area of the key + @param selected Whether to select or unselect the key + */ + void selectKey(touchArea area, bool selected); /** Set keyboard color @param a Alpha value @@ -150,10 +157,12 @@ private: int keyboardHeight; int activeLayer = 0; std::vector<KeyboardLayer> keyboard; + Uint32 format; Config *config; + TTF_Font *font = nullptr; /** - Draw keyboard row + Draw and store keyboard row @param surface Surface to draw on @param keyList List of keys for keyboard layout @param x X-axis coord. for start of row @@ -165,7 +174,7 @@ private: @param padding Spacing to reserve around the key @param font Font to use for key character */ - void drawRow(SDL_Surface *surface, std::vector<touchArea> &keyVector, int x, int y, + void drawAndStoreRow(SDL_Surface *surface, std::vector<touchArea> &keyVector, int x, int y, int width, int height, const std::vector<std::string> &keys, int padding, TTF_Font *font) const; @@ -176,9 +185,9 @@ private: void updateAnimations(); /** - Draw key for keyboard + Draw and store key for keyboard @param surface Surface to draw on - @param keyList List of keys for keyboard layout + @param keyVector List of keys for keyboard layout @param x X-axis coord. for start of row @param y Y-axis coord. for start of row @param width Width of row @@ -188,8 +197,24 @@ private: @param padding Spacing to reserve around the key @param font Font to use for key character */ - void drawKey(SDL_Surface *surface, std::vector<touchArea> &keyVector, int x, int y, - int width, int height, char *cap, const char *key, int padding, TTF_Font *font) const; + void drawAndStoreKey(SDL_Surface *surface, std::vector<touchArea> &keyVector, int x, int y, + int width, int height, const char *cap, const char *key, int padding, TTF_Font *font) const; + + /** + Draw key for keyboard + @param surface Surface to draw on + @param x X-axis coord. for start of row + @param y Y-axis coord. for start of row + @param width Width of row + @param height Height of row + @param cap Key cap + @param padding Spacing to reserve around the key + @param font Font to use for key character + @param bool Whether the key is selected or not + */ + void drawKey(SDL_Surface *surface, int x, int y, int width, int height, const char *cap, + int padding, TTF_Font *font, bool selected) const; + /** Prepare new keyboard @param layer Keyboard layer to use diff --git a/src/main.cpp b/src/main.cpp index 12c1988ece8dc9c0c506f8090a2cd383e78d6c43..5fbf313d13a9097a12e49449a4976bbfaf1684ce 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -139,7 +139,7 @@ int main(int argc, char **args) } // Initialize virtual keyboard - Keyboard keyboard(0, 1, WIDTH, keyboardHeight, &config); + Keyboard keyboard(0, 1, WIDTH, keyboardHeight, SDL_GetWindowPixelFormat(display), &config); keyboard.setKeyboardColor(0, 30, 30, 30); if (keyboard.init(renderer)) { fprintf(stderr, "ERROR: Failed to initialize keyboard!\n"); @@ -208,6 +208,7 @@ int main(int argc, char **args) // The Main Loop. bool done = false; + touchArea selectedKey = { "", 0, 0, 0, 0 }; while (luksDev.isLocked() && !done) { if (SDL_WaitEvent(&event)) { int cur_ticks = SDL_GetTicks(); @@ -247,6 +248,22 @@ int main(int argc, char **args) SDL_PushEvent(&renderEvent); break; // SDL_KEYDOWN // handle touchscreen + case SDL_FINGERDOWN: { + // x and y values are normalized! + auto xTouch = static_cast<unsigned>(event.tfinger.x * WIDTH); + auto yTouch = static_cast<unsigned>(event.tfinger.y * HEIGHT); + if (opts.verbose) { + printf("xTouch: %u\tyTouch: %u\n", xTouch, yTouch); + } + auto offsetYTouch = yTouch - static_cast<int>(HEIGHT - (keyboard.getHeight() * keyboard.getPosition())); + auto key = keyboard.getKeyForCoordinates(xTouch, offsetYTouch); + if (key.keyChar.length() > 0) { + keyboard.selectKey(key, true); + selectedKey = key; + } + SDL_PushEvent(&renderEvent); + break; // SDL_FINGERDOWN + } case SDL_FINGERUP: { showPasswordError = false; // x and y values are normalized! @@ -256,7 +273,11 @@ int main(int argc, char **args) printf("xTouch: %u\tyTouch: %u\n", xTouch, yTouch); } auto offsetYTouch = yTouch - static_cast<int>(HEIGHT - (keyboard.getHeight() * keyboard.getPosition())); - tapped = keyboard.getCharForCoordinates(xTouch, offsetYTouch); + auto key = keyboard.getKeyForCoordinates(xTouch, offsetYTouch); + if (selectedKey.keyChar.length() > 0) { + keyboard.selectKey(selectedKey, false); + } + tapped = key.keyChar; if (!luksDev.unlockRunning()) { done = handleVirtualKeyPress(tapped, keyboard, luksDev, passphrase, opts.keyscript); } @@ -264,6 +285,21 @@ int main(int argc, char **args) break; // SDL_FINGERUP } // handle the mouse + case SDL_MOUSEBUTTONDOWN: { + auto xMouse = event.button.x; + auto yMouse = event.button.y; + if (opts.verbose) { + printf("xMouse: %u\tyMouse: %u\n", xMouse, yMouse); + } + auto offsetYMouse = yMouse - static_cast<int>(HEIGHT - (keyboard.getHeight() * keyboard.getPosition())); + auto key = keyboard.getKeyForCoordinates(xMouse, offsetYMouse); + if (key.keyChar.length() > 0) { + keyboard.selectKey(key, true); + selectedKey = key; + } + SDL_PushEvent(&renderEvent); + break; // SDL_MOUSEBUTTONDOWN + } case SDL_MOUSEBUTTONUP: { showPasswordError = false; auto xMouse = event.button.x; @@ -272,7 +308,11 @@ int main(int argc, char **args) printf("xMouse: %u\tyMouse: %u\n", xMouse, yMouse); } auto offsetYMouse = yMouse - static_cast<int>(HEIGHT - (keyboard.getHeight() * keyboard.getPosition())); - tapped = keyboard.getCharForCoordinates(xMouse, offsetYMouse); + auto key = keyboard.getKeyForCoordinates(xMouse, offsetYMouse); + if (selectedKey.keyChar.length() > 0) { + keyboard.selectKey(selectedKey, false); + } + tapped = key.keyChar; if (!luksDev.unlockRunning()) { done = handleVirtualKeyPress(tapped, keyboard, luksDev, passphrase, opts.keyscript); }