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 "qemu/osdep.h" 19 20 #include <spice.h> 21 #include <spice/enums.h> 22 23 #include "ui/qemu-spice.h" 24 #include "ui/console.h" 25 #include "keymaps.h" 26 #include "ui/input.h" 27 28 /* keyboard bits */ 29 30 typedef struct QemuSpiceKbd { 31 SpiceKbdInstance sin; 32 int ledstate; 33 bool emul0; 34 size_t pauseseq; 35 } QemuSpiceKbd; 36 37 static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag); 38 static uint8_t kbd_get_leds(SpiceKbdInstance *sin); 39 40 static const SpiceKbdInterface kbd_interface = { 41 .base.type = SPICE_INTERFACE_KEYBOARD, 42 .base.description = "qemu keyboard", 43 .base.major_version = SPICE_INTERFACE_KEYBOARD_MAJOR, 44 .base.minor_version = SPICE_INTERFACE_KEYBOARD_MINOR, 45 .push_scan_freg = kbd_push_key, 46 .get_leds = kbd_get_leds, 47 }; 48 49 static void kbd_push_key(SpiceKbdInstance *sin, uint8_t scancode) 50 { 51 static const uint8_t pauseseq[] = { 0xe1, 0x1d, 0x45, 0xe1, 0x9d, 0xc5 }; 52 QemuSpiceKbd *kbd = container_of(sin, QemuSpiceKbd, sin); 53 int keycode; 54 bool up; 55 56 if (scancode == SCANCODE_EMUL0) { 57 kbd->emul0 = true; 58 return; 59 } 60 61 if (scancode == pauseseq[kbd->pauseseq]) { 62 kbd->pauseseq++; 63 if (kbd->pauseseq == G_N_ELEMENTS(pauseseq)) { 64 qemu_input_event_send_key_qcode(NULL, Q_KEY_CODE_PAUSE, true); 65 kbd->pauseseq = 0; 66 } 67 return; 68 } else { 69 kbd->pauseseq = 0; 70 } 71 72 keycode = scancode & ~SCANCODE_UP; 73 up = scancode & SCANCODE_UP; 74 if (kbd->emul0) { 75 kbd->emul0 = false; 76 keycode |= SCANCODE_GREY; 77 } 78 79 qemu_input_event_send_key_number(NULL, keycode, !up); 80 } 81 82 static uint8_t kbd_get_leds(SpiceKbdInstance *sin) 83 { 84 QemuSpiceKbd *kbd = container_of(sin, QemuSpiceKbd, sin); 85 return kbd->ledstate; 86 } 87 88 static void kbd_leds(void *opaque, int ledstate) 89 { 90 QemuSpiceKbd *kbd = opaque; 91 92 kbd->ledstate = 0; 93 if (ledstate & QEMU_SCROLL_LOCK_LED) { 94 kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK; 95 } 96 if (ledstate & QEMU_NUM_LOCK_LED) { 97 kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK; 98 } 99 if (ledstate & QEMU_CAPS_LOCK_LED) { 100 kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK; 101 } 102 spice_server_kbd_leds(&kbd->sin, kbd->ledstate); 103 } 104 105 /* mouse bits */ 106 107 typedef struct QemuSpicePointer { 108 SpiceMouseInstance mouse; 109 SpiceTabletInstance tablet; 110 int width, height; 111 uint32_t last_bmask; 112 Notifier mouse_mode; 113 bool absolute; 114 } QemuSpicePointer; 115 116 static void spice_update_buttons(QemuSpicePointer *pointer, 117 int wheel, uint32_t button_mask) 118 { 119 static uint32_t bmap[INPUT_BUTTON__MAX] = { 120 [INPUT_BUTTON_LEFT] = 0x01, 121 [INPUT_BUTTON_MIDDLE] = 0x04, 122 [INPUT_BUTTON_RIGHT] = 0x02, 123 [INPUT_BUTTON_WHEEL_UP] = 0x10, 124 [INPUT_BUTTON_WHEEL_DOWN] = 0x20, 125 [INPUT_BUTTON_SIDE] = 0x40, 126 [INPUT_BUTTON_EXTRA] = 0x80, 127 }; 128 129 if (wheel < 0) { 130 button_mask |= 0x10; 131 } 132 if (wheel > 0) { 133 button_mask |= 0x20; 134 } 135 136 if (pointer->last_bmask == button_mask) { 137 return; 138 } 139 qemu_input_update_buttons(NULL, bmap, pointer->last_bmask, button_mask); 140 pointer->last_bmask = button_mask; 141 } 142 143 static void mouse_motion(SpiceMouseInstance *sin, int dx, int dy, int dz, 144 uint32_t buttons_state) 145 { 146 QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, mouse); 147 spice_update_buttons(pointer, dz, buttons_state); 148 qemu_input_queue_rel(NULL, INPUT_AXIS_X, dx); 149 qemu_input_queue_rel(NULL, INPUT_AXIS_Y, dy); 150 qemu_input_event_sync(); 151 } 152 153 static void mouse_buttons(SpiceMouseInstance *sin, uint32_t buttons_state) 154 { 155 QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, mouse); 156 spice_update_buttons(pointer, 0, buttons_state); 157 qemu_input_event_sync(); 158 } 159 160 static const SpiceMouseInterface mouse_interface = { 161 .base.type = SPICE_INTERFACE_MOUSE, 162 .base.description = "mouse", 163 .base.major_version = SPICE_INTERFACE_MOUSE_MAJOR, 164 .base.minor_version = SPICE_INTERFACE_MOUSE_MINOR, 165 .motion = mouse_motion, 166 .buttons = mouse_buttons, 167 }; 168 169 static void tablet_set_logical_size(SpiceTabletInstance* sin, int width, int height) 170 { 171 QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); 172 173 if (height < 16) { 174 height = 16; 175 } 176 if (width < 16) { 177 width = 16; 178 } 179 pointer->width = width; 180 pointer->height = height; 181 } 182 183 static void tablet_position(SpiceTabletInstance* sin, int x, int y, 184 uint32_t buttons_state) 185 { 186 QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); 187 188 spice_update_buttons(pointer, 0, buttons_state); 189 qemu_input_queue_abs(NULL, INPUT_AXIS_X, x, 0, pointer->width); 190 qemu_input_queue_abs(NULL, INPUT_AXIS_Y, y, 0, pointer->height); 191 qemu_input_event_sync(); 192 } 193 194 195 static void tablet_wheel(SpiceTabletInstance* sin, int wheel, 196 uint32_t buttons_state) 197 { 198 QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); 199 200 spice_update_buttons(pointer, wheel, buttons_state); 201 qemu_input_event_sync(); 202 } 203 204 static void tablet_buttons(SpiceTabletInstance *sin, 205 uint32_t buttons_state) 206 { 207 QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); 208 209 spice_update_buttons(pointer, 0, buttons_state); 210 qemu_input_event_sync(); 211 } 212 213 static const SpiceTabletInterface tablet_interface = { 214 .base.type = SPICE_INTERFACE_TABLET, 215 .base.description = "tablet", 216 .base.major_version = SPICE_INTERFACE_TABLET_MAJOR, 217 .base.minor_version = SPICE_INTERFACE_TABLET_MINOR, 218 .set_logical_size = tablet_set_logical_size, 219 .position = tablet_position, 220 .wheel = tablet_wheel, 221 .buttons = tablet_buttons, 222 }; 223 224 static void mouse_mode_notifier(Notifier *notifier, void *data) 225 { 226 QemuSpicePointer *pointer = container_of(notifier, QemuSpicePointer, mouse_mode); 227 bool is_absolute = qemu_input_is_absolute(NULL); 228 229 if (pointer->absolute == is_absolute) { 230 return; 231 } 232 233 if (is_absolute) { 234 qemu_spice.add_interface(&pointer->tablet.base); 235 } else { 236 spice_server_remove_interface(&pointer->tablet.base); 237 } 238 pointer->absolute = is_absolute; 239 } 240 241 void qemu_spice_input_init(void) 242 { 243 QemuSpiceKbd *kbd; 244 QemuSpicePointer *pointer; 245 246 kbd = g_malloc0(sizeof(*kbd)); 247 kbd->sin.base.sif = &kbd_interface.base; 248 qemu_spice.add_interface(&kbd->sin.base); 249 qemu_add_led_event_handler(kbd_leds, kbd); 250 251 pointer = g_malloc0(sizeof(*pointer)); 252 pointer->mouse.base.sif = &mouse_interface.base; 253 pointer->tablet.base.sif = &tablet_interface.base; 254 qemu_spice.add_interface(&pointer->mouse.base); 255 256 pointer->absolute = false; 257 pointer->mouse_mode.notify = mouse_mode_notifier; 258 qemu_add_mouse_mode_change_notifier(&pointer->mouse_mode); 259 mouse_mode_notifier(&pointer->mouse_mode, NULL); 260 } 261