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 "qemu-common.h" 24 #include "ui/qemu-spice.h" 25 #include "ui/console.h" 26 #include "ui/keymaps.h" 27 #include "ui/input.h" 28 29 /* keyboard bits */ 30 31 typedef struct QemuSpiceKbd { 32 SpiceKbdInstance sin; 33 int ledstate; 34 bool emul0; 35 size_t pauseseq; 36 } QemuSpiceKbd; 37 38 static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag); 39 static uint8_t kbd_get_leds(SpiceKbdInstance *sin); 40 static void kbd_leds(void *opaque, int l); 41 42 static const SpiceKbdInterface kbd_interface = { 43 .base.type = SPICE_INTERFACE_KEYBOARD, 44 .base.description = "qemu keyboard", 45 .base.major_version = SPICE_INTERFACE_KEYBOARD_MAJOR, 46 .base.minor_version = SPICE_INTERFACE_KEYBOARD_MINOR, 47 .push_scan_freg = kbd_push_key, 48 .get_leds = kbd_get_leds, 49 }; 50 51 static void kbd_push_key(SpiceKbdInstance *sin, uint8_t scancode) 52 { 53 static const uint8_t pauseseq[] = { 0xe1, 0x1d, 0x45, 0xe1, 0x9d, 0xc5 }; 54 QemuSpiceKbd *kbd = container_of(sin, QemuSpiceKbd, sin); 55 int keycode; 56 bool up; 57 58 if (scancode == SCANCODE_EMUL0) { 59 kbd->emul0 = true; 60 return; 61 } 62 63 if (scancode == pauseseq[kbd->pauseseq]) { 64 kbd->pauseseq++; 65 if (kbd->pauseseq == G_N_ELEMENTS(pauseseq)) { 66 qemu_input_event_send_key_qcode(NULL, Q_KEY_CODE_PAUSE, true); 67 kbd->pauseseq = 0; 68 } 69 return; 70 } else { 71 kbd->pauseseq = 0; 72 } 73 74 keycode = scancode & ~SCANCODE_UP; 75 up = scancode & SCANCODE_UP; 76 if (kbd->emul0) { 77 kbd->emul0 = false; 78 keycode |= SCANCODE_GREY; 79 } 80 81 qemu_input_event_send_key_number(NULL, keycode, !up); 82 } 83 84 static uint8_t kbd_get_leds(SpiceKbdInstance *sin) 85 { 86 QemuSpiceKbd *kbd = container_of(sin, QemuSpiceKbd, sin); 87 return kbd->ledstate; 88 } 89 90 static void kbd_leds(void *opaque, int ledstate) 91 { 92 QemuSpiceKbd *kbd = opaque; 93 94 kbd->ledstate = 0; 95 if (ledstate & QEMU_SCROLL_LOCK_LED) { 96 kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK; 97 } 98 if (ledstate & QEMU_NUM_LOCK_LED) { 99 kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK; 100 } 101 if (ledstate & QEMU_CAPS_LOCK_LED) { 102 kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK; 103 } 104 spice_server_kbd_leds(&kbd->sin, kbd->ledstate); 105 } 106 107 /* mouse bits */ 108 109 typedef struct QemuSpicePointer { 110 SpiceMouseInstance mouse; 111 SpiceTabletInstance tablet; 112 int width, height; 113 uint32_t last_bmask; 114 Notifier mouse_mode; 115 bool absolute; 116 } QemuSpicePointer; 117 118 static void spice_update_buttons(QemuSpicePointer *pointer, 119 int wheel, uint32_t button_mask) 120 { 121 static uint32_t bmap[INPUT_BUTTON__MAX] = { 122 [INPUT_BUTTON_LEFT] = 0x01, 123 [INPUT_BUTTON_MIDDLE] = 0x04, 124 [INPUT_BUTTON_RIGHT] = 0x02, 125 [INPUT_BUTTON_WHEEL_UP] = 0x10, 126 [INPUT_BUTTON_WHEEL_DOWN] = 0x20, 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(); 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