1 /* 2 * Copyright (C) 2010 Red Hat, Inc. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 or 7 * (at your option) version 3 of the License. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include <stdlib.h> 19 #include <stdio.h> 20 #include <stdbool.h> 21 #include <string.h> 22 23 #include <spice.h> 24 #include <spice/enums.h> 25 26 #include "qemu-common.h" 27 #include "ui/qemu-spice.h" 28 #include "ui/console.h" 29 #include "ui/keymaps.h" 30 #include "ui/input.h" 31 32 /* keyboard bits */ 33 34 typedef struct QemuSpiceKbd { 35 SpiceKbdInstance sin; 36 int ledstate; 37 bool emul0; 38 } QemuSpiceKbd; 39 40 static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag); 41 static uint8_t kbd_get_leds(SpiceKbdInstance *sin); 42 static void kbd_leds(void *opaque, int l); 43 44 static const SpiceKbdInterface kbd_interface = { 45 .base.type = SPICE_INTERFACE_KEYBOARD, 46 .base.description = "qemu keyboard", 47 .base.major_version = SPICE_INTERFACE_KEYBOARD_MAJOR, 48 .base.minor_version = SPICE_INTERFACE_KEYBOARD_MINOR, 49 .push_scan_freg = kbd_push_key, 50 .get_leds = kbd_get_leds, 51 }; 52 53 static void kbd_push_key(SpiceKbdInstance *sin, uint8_t scancode) 54 { 55 QemuSpiceKbd *kbd = container_of(sin, QemuSpiceKbd, sin); 56 int keycode; 57 bool up; 58 59 if (scancode == SCANCODE_EMUL0) { 60 kbd->emul0 = true; 61 return; 62 } 63 keycode = scancode & ~SCANCODE_UP; 64 up = scancode & SCANCODE_UP; 65 if (kbd->emul0) { 66 kbd->emul0 = false; 67 keycode |= SCANCODE_GREY; 68 } 69 70 qemu_input_event_send_key_number(NULL, keycode, !up); 71 } 72 73 static uint8_t kbd_get_leds(SpiceKbdInstance *sin) 74 { 75 QemuSpiceKbd *kbd = container_of(sin, QemuSpiceKbd, sin); 76 return kbd->ledstate; 77 } 78 79 static void kbd_leds(void *opaque, int ledstate) 80 { 81 QemuSpiceKbd *kbd = opaque; 82 83 kbd->ledstate = 0; 84 if (ledstate & QEMU_SCROLL_LOCK_LED) { 85 kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK; 86 } 87 if (ledstate & QEMU_NUM_LOCK_LED) { 88 kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK; 89 } 90 if (ledstate & QEMU_CAPS_LOCK_LED) { 91 kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK; 92 } 93 spice_server_kbd_leds(&kbd->sin, ledstate); 94 } 95 96 /* mouse bits */ 97 98 typedef struct QemuSpicePointer { 99 SpiceMouseInstance mouse; 100 SpiceTabletInstance tablet; 101 int width, height; 102 uint32_t last_bmask; 103 Notifier mouse_mode; 104 bool absolute; 105 } QemuSpicePointer; 106 107 static void spice_update_buttons(QemuSpicePointer *pointer, 108 int wheel, uint32_t button_mask) 109 { 110 static uint32_t bmap[INPUT_BUTTON_MAX] = { 111 [INPUT_BUTTON_LEFT] = 0x01, 112 [INPUT_BUTTON_MIDDLE] = 0x04, 113 [INPUT_BUTTON_RIGHT] = 0x02, 114 [INPUT_BUTTON_WHEEL_UP] = 0x10, 115 [INPUT_BUTTON_WHEEL_DOWN] = 0x20, 116 }; 117 118 if (wheel < 0) { 119 button_mask |= 0x10; 120 } 121 if (wheel > 0) { 122 button_mask |= 0x20; 123 } 124 125 if (pointer->last_bmask == button_mask) { 126 return; 127 } 128 qemu_input_update_buttons(NULL, bmap, pointer->last_bmask, button_mask); 129 pointer->last_bmask = button_mask; 130 } 131 132 static void mouse_motion(SpiceMouseInstance *sin, int dx, int dy, int dz, 133 uint32_t buttons_state) 134 { 135 QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, mouse); 136 spice_update_buttons(pointer, dz, buttons_state); 137 qemu_input_queue_rel(NULL, INPUT_AXIS_X, dx); 138 qemu_input_queue_rel(NULL, INPUT_AXIS_Y, dy); 139 qemu_input_event_sync(); 140 } 141 142 static void mouse_buttons(SpiceMouseInstance *sin, uint32_t buttons_state) 143 { 144 QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, mouse); 145 spice_update_buttons(pointer, 0, buttons_state); 146 qemu_input_event_sync(); 147 } 148 149 static const SpiceMouseInterface mouse_interface = { 150 .base.type = SPICE_INTERFACE_MOUSE, 151 .base.description = "mouse", 152 .base.major_version = SPICE_INTERFACE_MOUSE_MAJOR, 153 .base.minor_version = SPICE_INTERFACE_MOUSE_MINOR, 154 .motion = mouse_motion, 155 .buttons = mouse_buttons, 156 }; 157 158 static void tablet_set_logical_size(SpiceTabletInstance* sin, int width, int height) 159 { 160 QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); 161 162 if (height < 16) { 163 height = 16; 164 } 165 if (width < 16) { 166 width = 16; 167 } 168 pointer->width = width; 169 pointer->height = height; 170 } 171 172 static void tablet_position(SpiceTabletInstance* sin, int x, int y, 173 uint32_t buttons_state) 174 { 175 QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); 176 177 spice_update_buttons(pointer, 0, buttons_state); 178 qemu_input_queue_abs(NULL, INPUT_AXIS_X, x, pointer->width); 179 qemu_input_queue_abs(NULL, INPUT_AXIS_Y, y, pointer->height); 180 qemu_input_event_sync(); 181 } 182 183 184 static void tablet_wheel(SpiceTabletInstance* sin, int wheel, 185 uint32_t buttons_state) 186 { 187 QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); 188 189 spice_update_buttons(pointer, wheel, buttons_state); 190 qemu_input_event_sync(); 191 } 192 193 static void tablet_buttons(SpiceTabletInstance *sin, 194 uint32_t buttons_state) 195 { 196 QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); 197 198 spice_update_buttons(pointer, 0, buttons_state); 199 qemu_input_event_sync(); 200 } 201 202 static const SpiceTabletInterface tablet_interface = { 203 .base.type = SPICE_INTERFACE_TABLET, 204 .base.description = "tablet", 205 .base.major_version = SPICE_INTERFACE_TABLET_MAJOR, 206 .base.minor_version = SPICE_INTERFACE_TABLET_MINOR, 207 .set_logical_size = tablet_set_logical_size, 208 .position = tablet_position, 209 .wheel = tablet_wheel, 210 .buttons = tablet_buttons, 211 }; 212 213 static void mouse_mode_notifier(Notifier *notifier, void *data) 214 { 215 QemuSpicePointer *pointer = container_of(notifier, QemuSpicePointer, mouse_mode); 216 bool is_absolute = qemu_input_is_absolute(); 217 218 if (pointer->absolute == is_absolute) { 219 return; 220 } 221 222 if (is_absolute) { 223 qemu_spice_add_interface(&pointer->tablet.base); 224 } else { 225 spice_server_remove_interface(&pointer->tablet.base); 226 } 227 pointer->absolute = is_absolute; 228 } 229 230 void qemu_spice_input_init(void) 231 { 232 QemuSpiceKbd *kbd; 233 QemuSpicePointer *pointer; 234 235 kbd = g_malloc0(sizeof(*kbd)); 236 kbd->sin.base.sif = &kbd_interface.base; 237 qemu_spice_add_interface(&kbd->sin.base); 238 qemu_add_led_event_handler(kbd_leds, kbd); 239 240 pointer = g_malloc0(sizeof(*pointer)); 241 pointer->mouse.base.sif = &mouse_interface.base; 242 pointer->tablet.base.sif = &tablet_interface.base; 243 qemu_spice_add_interface(&pointer->mouse.base); 244 245 pointer->absolute = false; 246 pointer->mouse_mode.notify = mouse_mode_notifier; 247 qemu_add_mouse_mode_change_notifier(&pointer->mouse_mode); 248 mouse_mode_notifier(&pointer->mouse_mode, NULL); 249 } 250