1864401c2SGerd Hoffmann /* 2864401c2SGerd Hoffmann * Copyright (C) 2010 Red Hat, Inc. 3864401c2SGerd Hoffmann * 4864401c2SGerd Hoffmann * This program is free software; you can redistribute it and/or 5864401c2SGerd Hoffmann * modify it under the terms of the GNU General Public License as 6864401c2SGerd Hoffmann * published by the Free Software Foundation; either version 2 or 7864401c2SGerd Hoffmann * (at your option) version 3 of the License. 8864401c2SGerd Hoffmann * 9864401c2SGerd Hoffmann * This program is distributed in the hope that it will be useful, 10864401c2SGerd Hoffmann * but WITHOUT ANY WARRANTY; without even the implied warranty of 11864401c2SGerd Hoffmann * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12864401c2SGerd Hoffmann * GNU General Public License for more details. 13864401c2SGerd Hoffmann * 14864401c2SGerd Hoffmann * You should have received a copy of the GNU General Public License 15864401c2SGerd Hoffmann * along with this program; if not, see <http://www.gnu.org/licenses/>. 16864401c2SGerd Hoffmann */ 17864401c2SGerd Hoffmann 18e16f4c87SPeter Maydell #include "qemu/osdep.h" 19864401c2SGerd Hoffmann 20864401c2SGerd Hoffmann #include <spice.h> 21864401c2SGerd Hoffmann #include <spice/enums.h> 22864401c2SGerd Hoffmann 23864401c2SGerd Hoffmann #include "qemu-common.h" 2428ecbaeeSPaolo Bonzini #include "ui/qemu-spice.h" 2528ecbaeeSPaolo Bonzini #include "ui/console.h" 26de8f580bSGerd Hoffmann #include "ui/keymaps.h" 27de8f580bSGerd Hoffmann #include "ui/input.h" 28864401c2SGerd Hoffmann 29864401c2SGerd Hoffmann /* keyboard bits */ 30864401c2SGerd Hoffmann 31864401c2SGerd Hoffmann typedef struct QemuSpiceKbd { 32864401c2SGerd Hoffmann SpiceKbdInstance sin; 33864401c2SGerd Hoffmann int ledstate; 34de8f580bSGerd Hoffmann bool emul0; 35*7c388dbdSDaniel P. Berrange size_t pauseseq; 36864401c2SGerd Hoffmann } QemuSpiceKbd; 37864401c2SGerd Hoffmann 38864401c2SGerd Hoffmann static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag); 39864401c2SGerd Hoffmann static uint8_t kbd_get_leds(SpiceKbdInstance *sin); 40864401c2SGerd Hoffmann static void kbd_leds(void *opaque, int l); 41864401c2SGerd Hoffmann 42864401c2SGerd Hoffmann static const SpiceKbdInterface kbd_interface = { 43864401c2SGerd Hoffmann .base.type = SPICE_INTERFACE_KEYBOARD, 44864401c2SGerd Hoffmann .base.description = "qemu keyboard", 45864401c2SGerd Hoffmann .base.major_version = SPICE_INTERFACE_KEYBOARD_MAJOR, 46864401c2SGerd Hoffmann .base.minor_version = SPICE_INTERFACE_KEYBOARD_MINOR, 47864401c2SGerd Hoffmann .push_scan_freg = kbd_push_key, 48864401c2SGerd Hoffmann .get_leds = kbd_get_leds, 49864401c2SGerd Hoffmann }; 50864401c2SGerd Hoffmann 51de8f580bSGerd Hoffmann static void kbd_push_key(SpiceKbdInstance *sin, uint8_t scancode) 52864401c2SGerd Hoffmann { 53de8f580bSGerd Hoffmann QemuSpiceKbd *kbd = container_of(sin, QemuSpiceKbd, sin); 54de8f580bSGerd Hoffmann int keycode; 55de8f580bSGerd Hoffmann bool up; 56de8f580bSGerd Hoffmann 57de8f580bSGerd Hoffmann if (scancode == SCANCODE_EMUL0) { 58de8f580bSGerd Hoffmann kbd->emul0 = true; 59de8f580bSGerd Hoffmann return; 60de8f580bSGerd Hoffmann } 61de8f580bSGerd Hoffmann keycode = scancode & ~SCANCODE_UP; 62de8f580bSGerd Hoffmann up = scancode & SCANCODE_UP; 63de8f580bSGerd Hoffmann if (kbd->emul0) { 64de8f580bSGerd Hoffmann kbd->emul0 = false; 65de8f580bSGerd Hoffmann keycode |= SCANCODE_GREY; 66de8f580bSGerd Hoffmann } 67de8f580bSGerd Hoffmann 68*7c388dbdSDaniel P. Berrange if (scancode == SCANCODE_EMUL1) { 69*7c388dbdSDaniel P. Berrange kbd->pauseseq++; 70*7c388dbdSDaniel P. Berrange return; 71*7c388dbdSDaniel P. Berrange } else if (kbd->pauseseq == 1) { 72*7c388dbdSDaniel P. Berrange if (keycode == 0x1d) { 73*7c388dbdSDaniel P. Berrange kbd->pauseseq++; 74*7c388dbdSDaniel P. Berrange return; 75*7c388dbdSDaniel P. Berrange } else { 76*7c388dbdSDaniel P. Berrange kbd->pauseseq = 0; 77*7c388dbdSDaniel P. Berrange } 78*7c388dbdSDaniel P. Berrange } else if (kbd->pauseseq == 2) { 79*7c388dbdSDaniel P. Berrange if (keycode == 0x45) { 80*7c388dbdSDaniel P. Berrange qemu_input_event_send_key_qcode(NULL, Q_KEY_CODE_PAUSE, !up); 81*7c388dbdSDaniel P. Berrange kbd->pauseseq = 0; 82*7c388dbdSDaniel P. Berrange return; 83*7c388dbdSDaniel P. Berrange } 84*7c388dbdSDaniel P. Berrange kbd->pauseseq = 0; 85*7c388dbdSDaniel P. Berrange } 86*7c388dbdSDaniel P. Berrange 87de8f580bSGerd Hoffmann qemu_input_event_send_key_number(NULL, keycode, !up); 88864401c2SGerd Hoffmann } 89864401c2SGerd Hoffmann 90864401c2SGerd Hoffmann static uint8_t kbd_get_leds(SpiceKbdInstance *sin) 91864401c2SGerd Hoffmann { 92864401c2SGerd Hoffmann QemuSpiceKbd *kbd = container_of(sin, QemuSpiceKbd, sin); 93864401c2SGerd Hoffmann return kbd->ledstate; 94864401c2SGerd Hoffmann } 95864401c2SGerd Hoffmann 96864401c2SGerd Hoffmann static void kbd_leds(void *opaque, int ledstate) 97864401c2SGerd Hoffmann { 98864401c2SGerd Hoffmann QemuSpiceKbd *kbd = opaque; 99864401c2SGerd Hoffmann 100864401c2SGerd Hoffmann kbd->ledstate = 0; 101864401c2SGerd Hoffmann if (ledstate & QEMU_SCROLL_LOCK_LED) { 102864401c2SGerd Hoffmann kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK; 103864401c2SGerd Hoffmann } 104864401c2SGerd Hoffmann if (ledstate & QEMU_NUM_LOCK_LED) { 105864401c2SGerd Hoffmann kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK; 106864401c2SGerd Hoffmann } 107864401c2SGerd Hoffmann if (ledstate & QEMU_CAPS_LOCK_LED) { 108864401c2SGerd Hoffmann kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK; 109864401c2SGerd Hoffmann } 110bfefa6d7SJonathon Jongsma spice_server_kbd_leds(&kbd->sin, kbd->ledstate); 111864401c2SGerd Hoffmann } 112864401c2SGerd Hoffmann 11378dd9ac1SGerd Hoffmann /* mouse bits */ 11478dd9ac1SGerd Hoffmann 115869564a9SGerd Hoffmann typedef struct QemuSpicePointer { 116869564a9SGerd Hoffmann SpiceMouseInstance mouse; 117869564a9SGerd Hoffmann SpiceTabletInstance tablet; 118f100db38SGerd Hoffmann int width, height; 119f100db38SGerd Hoffmann uint32_t last_bmask; 120869564a9SGerd Hoffmann Notifier mouse_mode; 121869564a9SGerd Hoffmann bool absolute; 122869564a9SGerd Hoffmann } QemuSpicePointer; 12378dd9ac1SGerd Hoffmann 124f100db38SGerd Hoffmann static void spice_update_buttons(QemuSpicePointer *pointer, 125f100db38SGerd Hoffmann int wheel, uint32_t button_mask) 12678dd9ac1SGerd Hoffmann { 1277fb1cf16SEric Blake static uint32_t bmap[INPUT_BUTTON__MAX] = { 128f100db38SGerd Hoffmann [INPUT_BUTTON_LEFT] = 0x01, 129f100db38SGerd Hoffmann [INPUT_BUTTON_MIDDLE] = 0x04, 130f100db38SGerd Hoffmann [INPUT_BUTTON_RIGHT] = 0x02, 131f22d0af0SGerd Hoffmann [INPUT_BUTTON_WHEEL_UP] = 0x10, 132f22d0af0SGerd Hoffmann [INPUT_BUTTON_WHEEL_DOWN] = 0x20, 133f100db38SGerd Hoffmann }; 13478dd9ac1SGerd Hoffmann 135f100db38SGerd Hoffmann if (wheel < 0) { 136f100db38SGerd Hoffmann button_mask |= 0x10; 13778dd9ac1SGerd Hoffmann } 138f100db38SGerd Hoffmann if (wheel > 0) { 139f100db38SGerd Hoffmann button_mask |= 0x20; 14078dd9ac1SGerd Hoffmann } 141f100db38SGerd Hoffmann 142f100db38SGerd Hoffmann if (pointer->last_bmask == button_mask) { 143f100db38SGerd Hoffmann return; 14478dd9ac1SGerd Hoffmann } 145f100db38SGerd Hoffmann qemu_input_update_buttons(NULL, bmap, pointer->last_bmask, button_mask); 146f100db38SGerd Hoffmann pointer->last_bmask = button_mask; 14778dd9ac1SGerd Hoffmann } 14878dd9ac1SGerd Hoffmann 14978dd9ac1SGerd Hoffmann static void mouse_motion(SpiceMouseInstance *sin, int dx, int dy, int dz, 15078dd9ac1SGerd Hoffmann uint32_t buttons_state) 15178dd9ac1SGerd Hoffmann { 152f100db38SGerd Hoffmann QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, mouse); 153f100db38SGerd Hoffmann spice_update_buttons(pointer, dz, buttons_state); 154f100db38SGerd Hoffmann qemu_input_queue_rel(NULL, INPUT_AXIS_X, dx); 155f100db38SGerd Hoffmann qemu_input_queue_rel(NULL, INPUT_AXIS_Y, dy); 156f100db38SGerd Hoffmann qemu_input_event_sync(); 15778dd9ac1SGerd Hoffmann } 15878dd9ac1SGerd Hoffmann 15978dd9ac1SGerd Hoffmann static void mouse_buttons(SpiceMouseInstance *sin, uint32_t buttons_state) 16078dd9ac1SGerd Hoffmann { 161f100db38SGerd Hoffmann QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, mouse); 162f100db38SGerd Hoffmann spice_update_buttons(pointer, 0, buttons_state); 163f100db38SGerd Hoffmann qemu_input_event_sync(); 16478dd9ac1SGerd Hoffmann } 16578dd9ac1SGerd Hoffmann 16678dd9ac1SGerd Hoffmann static const SpiceMouseInterface mouse_interface = { 16778dd9ac1SGerd Hoffmann .base.type = SPICE_INTERFACE_MOUSE, 16878dd9ac1SGerd Hoffmann .base.description = "mouse", 16978dd9ac1SGerd Hoffmann .base.major_version = SPICE_INTERFACE_MOUSE_MAJOR, 17078dd9ac1SGerd Hoffmann .base.minor_version = SPICE_INTERFACE_MOUSE_MINOR, 17178dd9ac1SGerd Hoffmann .motion = mouse_motion, 17278dd9ac1SGerd Hoffmann .buttons = mouse_buttons, 17378dd9ac1SGerd Hoffmann }; 17478dd9ac1SGerd Hoffmann 175869564a9SGerd Hoffmann static void tablet_set_logical_size(SpiceTabletInstance* sin, int width, int height) 176869564a9SGerd Hoffmann { 177869564a9SGerd Hoffmann QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); 178869564a9SGerd Hoffmann 179869564a9SGerd Hoffmann if (height < 16) { 180869564a9SGerd Hoffmann height = 16; 181869564a9SGerd Hoffmann } 182869564a9SGerd Hoffmann if (width < 16) { 183869564a9SGerd Hoffmann width = 16; 184869564a9SGerd Hoffmann } 185869564a9SGerd Hoffmann pointer->width = width; 186869564a9SGerd Hoffmann pointer->height = height; 187869564a9SGerd Hoffmann } 188869564a9SGerd Hoffmann 189869564a9SGerd Hoffmann static void tablet_position(SpiceTabletInstance* sin, int x, int y, 190869564a9SGerd Hoffmann uint32_t buttons_state) 191869564a9SGerd Hoffmann { 192869564a9SGerd Hoffmann QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); 193869564a9SGerd Hoffmann 194f100db38SGerd Hoffmann spice_update_buttons(pointer, 0, buttons_state); 1959cfa7ab9SPhilippe Voinov qemu_input_queue_abs(NULL, INPUT_AXIS_X, x, 0, pointer->width); 1969cfa7ab9SPhilippe Voinov qemu_input_queue_abs(NULL, INPUT_AXIS_Y, y, 0, pointer->height); 197f100db38SGerd Hoffmann qemu_input_event_sync(); 198869564a9SGerd Hoffmann } 199869564a9SGerd Hoffmann 200869564a9SGerd Hoffmann 201869564a9SGerd Hoffmann static void tablet_wheel(SpiceTabletInstance* sin, int wheel, 202869564a9SGerd Hoffmann uint32_t buttons_state) 203869564a9SGerd Hoffmann { 204869564a9SGerd Hoffmann QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); 205869564a9SGerd Hoffmann 206f100db38SGerd Hoffmann spice_update_buttons(pointer, wheel, buttons_state); 207f100db38SGerd Hoffmann qemu_input_event_sync(); 208869564a9SGerd Hoffmann } 209869564a9SGerd Hoffmann 210869564a9SGerd Hoffmann static void tablet_buttons(SpiceTabletInstance *sin, 211869564a9SGerd Hoffmann uint32_t buttons_state) 212869564a9SGerd Hoffmann { 213869564a9SGerd Hoffmann QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); 214869564a9SGerd Hoffmann 215f100db38SGerd Hoffmann spice_update_buttons(pointer, 0, buttons_state); 216f100db38SGerd Hoffmann qemu_input_event_sync(); 217869564a9SGerd Hoffmann } 218869564a9SGerd Hoffmann 219869564a9SGerd Hoffmann static const SpiceTabletInterface tablet_interface = { 220869564a9SGerd Hoffmann .base.type = SPICE_INTERFACE_TABLET, 221869564a9SGerd Hoffmann .base.description = "tablet", 222869564a9SGerd Hoffmann .base.major_version = SPICE_INTERFACE_TABLET_MAJOR, 223869564a9SGerd Hoffmann .base.minor_version = SPICE_INTERFACE_TABLET_MINOR, 224869564a9SGerd Hoffmann .set_logical_size = tablet_set_logical_size, 225869564a9SGerd Hoffmann .position = tablet_position, 226869564a9SGerd Hoffmann .wheel = tablet_wheel, 227869564a9SGerd Hoffmann .buttons = tablet_buttons, 228869564a9SGerd Hoffmann }; 229869564a9SGerd Hoffmann 2309e8dd451SJan Kiszka static void mouse_mode_notifier(Notifier *notifier, void *data) 231869564a9SGerd Hoffmann { 232869564a9SGerd Hoffmann QemuSpicePointer *pointer = container_of(notifier, QemuSpicePointer, mouse_mode); 233f100db38SGerd Hoffmann bool is_absolute = qemu_input_is_absolute(); 234869564a9SGerd Hoffmann 235869564a9SGerd Hoffmann if (pointer->absolute == is_absolute) { 236869564a9SGerd Hoffmann return; 237869564a9SGerd Hoffmann } 238869564a9SGerd Hoffmann 239869564a9SGerd Hoffmann if (is_absolute) { 240869564a9SGerd Hoffmann qemu_spice_add_interface(&pointer->tablet.base); 241869564a9SGerd Hoffmann } else { 242869564a9SGerd Hoffmann spice_server_remove_interface(&pointer->tablet.base); 243869564a9SGerd Hoffmann } 244869564a9SGerd Hoffmann pointer->absolute = is_absolute; 245869564a9SGerd Hoffmann } 246869564a9SGerd Hoffmann 247864401c2SGerd Hoffmann void qemu_spice_input_init(void) 248864401c2SGerd Hoffmann { 249864401c2SGerd Hoffmann QemuSpiceKbd *kbd; 250869564a9SGerd Hoffmann QemuSpicePointer *pointer; 251864401c2SGerd Hoffmann 2527267c094SAnthony Liguori kbd = g_malloc0(sizeof(*kbd)); 253864401c2SGerd Hoffmann kbd->sin.base.sif = &kbd_interface.base; 254864401c2SGerd Hoffmann qemu_spice_add_interface(&kbd->sin.base); 255864401c2SGerd Hoffmann qemu_add_led_event_handler(kbd_leds, kbd); 25678dd9ac1SGerd Hoffmann 2577267c094SAnthony Liguori pointer = g_malloc0(sizeof(*pointer)); 258869564a9SGerd Hoffmann pointer->mouse.base.sif = &mouse_interface.base; 259869564a9SGerd Hoffmann pointer->tablet.base.sif = &tablet_interface.base; 260869564a9SGerd Hoffmann qemu_spice_add_interface(&pointer->mouse.base); 261869564a9SGerd Hoffmann 262869564a9SGerd Hoffmann pointer->absolute = false; 263869564a9SGerd Hoffmann pointer->mouse_mode.notify = mouse_mode_notifier; 264869564a9SGerd Hoffmann qemu_add_mouse_mode_change_notifier(&pointer->mouse_mode); 2659e8dd451SJan Kiszka mouse_mode_notifier(&pointer->mouse_mode, NULL); 266864401c2SGerd Hoffmann } 267