/** * Looking Glass * Copyright © 2017-2026 The Looking Glass Authors * https://looking-glass.io * * This program 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. * * This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "input.h" #include "kb.h" #include "common/locking.h" #include #include #include struct SpiceInput { LG_Lock stateLock; LG_Lock statusDispatch; bool available; uint32_t statusGeneration; LG_InputStatusFn statusCallback; void * statusOpaque; }; static uint32_t nextGeneration(uint32_t generation) { if (++generation == 0) ++generation; return generation; } static bool spiceSupports(void * opaque, LG_InputSupport support) { (void)opaque; (void)support; return false; } static void spiceSetStatusListener(void * opaque, LG_InputStatusFn callback, void * callbackOpaque) { SpiceInput * input = opaque; LG_LOCK(input->statusDispatch); LG_LOCK(input->stateLock); input->statusCallback = callback; input->statusOpaque = callbackOpaque; const LG_InputStatus status = { .available = input->available, .generation = input->statusGeneration, }; LG_UNLOCK(input->stateLock); if (callback) callback(callbackOpaque, &status); LG_UNLOCK(input->statusDispatch); } static bool spiceKeyDown(void * opaque, int key) { SpiceInput * input = opaque; const uint32_t ps2 = linux_to_ps2[key]; LG_LOCK(input->stateLock); const bool result = input->available && (!ps2 || purespice_keyDown(ps2)); LG_UNLOCK(input->stateLock); return result; } static bool spiceKeyUp(void * opaque, int key) { SpiceInput * input = opaque; const uint32_t ps2 = linux_to_ps2[key]; LG_LOCK(input->stateLock); const bool result = !ps2 || purespice_keyUp(ps2); LG_UNLOCK(input->stateLock); return result; } static bool spiceKeyboardLEDs(void * opaque, bool numLock, bool capsLock, bool scrollLock) { SpiceInput * input = opaque; const uint32_t modifiers = (scrollLock ? 1 /* SPICE_SCROLL_LOCK_MODIFIER */ : 0) | (numLock ? 2 /* SPICE_NUM_LOCK_MODIFIER */ : 0) | (capsLock ? 4 /* SPICE_CAPS_LOCK_MODIFIER */ : 0); LG_LOCK(input->stateLock); const bool result = input->available && purespice_keyModifiers(modifiers); LG_UNLOCK(input->stateLock); return result; } static bool spiceMouseMotion(void * opaque, int32_t x, int32_t y) { SpiceInput * input = opaque; LG_LOCK(input->stateLock); const bool result = input->available && purespice_mouseMotion(x, y); LG_UNLOCK(input->stateLock); return result; } static bool spiceMousePress(void * opaque, unsigned int button) { if (button > 7) return true; SpiceInput * input = opaque; LG_LOCK(input->stateLock); const bool result = input->available && purespice_mousePress(button); LG_UNLOCK(input->stateLock); return result; } static bool spiceMouseRelease(void * opaque, unsigned int button) { if (button > 7) return true; SpiceInput * input = opaque; LG_LOCK(input->stateLock); const bool result = purespice_mouseRelease(button); LG_UNLOCK(input->stateLock); return result; } static const LG_InputOps l_inputOps = { .name = "SPICE", .supports = spiceSupports, .setStatusListener = spiceSetStatusListener, .keyDown = spiceKeyDown, .keyUp = spiceKeyUp, .keyboardLEDs = spiceKeyboardLEDs, .mouseMotion = spiceMouseMotion, .mousePosition = NULL, .mousePress = spiceMousePress, .mouseRelease = spiceMouseRelease, .reset = NULL, }; bool spiceInput_init(SpiceInput ** input) { *input = calloc(1, sizeof(**input)); if (!*input) return false; LG_LOCK_INIT((*input)->stateLock); LG_LOCK_INIT((*input)->statusDispatch); return true; } void spiceInput_free(SpiceInput ** input) { if (!input || !*input) return; LG_LOCK_FREE((*input)->statusDispatch); LG_LOCK_FREE((*input)->stateLock); free(*input); *input = NULL; } const LG_InputOps * spiceInput_getOps(void) { return &l_inputOps; } void spiceInput_setAvailable(SpiceInput * input, bool available) { LG_LOCK(input->statusDispatch); LG_LOCK(input->stateLock); const bool changed = input->available != available; if (changed) { input->available = available; input->statusGeneration = nextGeneration(input->statusGeneration); } const LG_InputStatusFn callback = input->statusCallback; void * callbackOpaque = input->statusOpaque; const LG_InputStatus status = { .available = input->available, .generation = input->statusGeneration, }; LG_UNLOCK(input->stateLock); if (changed && callback) callback(callbackOpaque, &status); LG_UNLOCK(input->statusDispatch); }