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