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 }; 127 128 if (wheel < 0) { 129 button_mask |= 0x10; 130 } 131 if (wheel > 0) { 132 button_mask |= 0x20; 133 } 134 135 if (pointer->last_bmask == button_mask) { 136 return; 137 } 138 qemu_input_update_buttons(NULL, bmap, pointer->last_bmask, button_mask); 139 pointer->last_bmask = button_mask; 140 } 141 142 static void mouse_motion(SpiceMouseInstance *sin, int dx, int dy, int dz, 143 uint32_t buttons_state) 144 { 145 QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, mouse); 146 spice_update_buttons(pointer, dz, buttons_state); 147 qemu_input_queue_rel(NULL, INPUT_AXIS_X, dx); 148 qemu_input_queue_rel(NULL, INPUT_AXIS_Y, dy); 149 qemu_input_event_sync(); 150 } 151 152 static void mouse_buttons(SpiceMouseInstance *sin, uint32_t buttons_state) 153 { 154 QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, mouse); 155 spice_update_buttons(pointer, 0, buttons_state); 156 qemu_input_event_sync(); 157 } 158 159 static const SpiceMouseInterface mouse_interface = { 160 .base.type = SPICE_INTERFACE_MOUSE, 161 .base.description = "mouse", 162 .base.major_version = SPICE_INTERFACE_MOUSE_MAJOR, 163 .base.minor_version = SPICE_INTERFACE_MOUSE_MINOR, 164 .motion = mouse_motion, 165 .buttons = mouse_buttons, 166 }; 167 168 static void tablet_set_logical_size(SpiceTabletInstance* sin, int width, int height) 169 { 170 QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); 171 172 if (height < 16) { 173 height = 16; 174 } 175 if (width < 16) { 176 width = 16; 177 } 178 pointer->width = width; 179 pointer->height = height; 180 } 181 182 static void tablet_position(SpiceTabletInstance* sin, int x, int y, 183 uint32_t buttons_state) 184 { 185 QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); 186 187 spice_update_buttons(pointer, 0, buttons_state); 188 qemu_input_queue_abs(NULL, INPUT_AXIS_X, x, 0, pointer->width); 189 qemu_input_queue_abs(NULL, INPUT_AXIS_Y, y, 0, pointer->height); 190 qemu_input_event_sync(); 191 } 192 193 194 static void tablet_wheel(SpiceTabletInstance* sin, int wheel, 195 uint32_t buttons_state) 196 { 197 QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); 198 199 spice_update_buttons(pointer, wheel, buttons_state); 200 qemu_input_event_sync(); 201 } 202 203 static void tablet_buttons(SpiceTabletInstance *sin, 204 uint32_t buttons_state) 205 { 206 QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); 207 208 spice_update_buttons(pointer, 0, buttons_state); 209 qemu_input_event_sync(); 210 } 211 212 static const SpiceTabletInterface tablet_interface = { 213 .base.type = SPICE_INTERFACE_TABLET, 214 .base.description = "tablet", 215 .base.major_version = SPICE_INTERFACE_TABLET_MAJOR, 216 .base.minor_version = SPICE_INTERFACE_TABLET_MINOR, 217 .set_logical_size = tablet_set_logical_size, 218 .position = tablet_position, 219 .wheel = tablet_wheel, 220 .buttons = tablet_buttons, 221 }; 222 223 static void mouse_mode_notifier(Notifier *notifier, void *data) 224 { 225 QemuSpicePointer *pointer = container_of(notifier, QemuSpicePointer, mouse_mode); 226 bool is_absolute = qemu_input_is_absolute(); 227 228 if (pointer->absolute == is_absolute) { 229 return; 230 } 231 232 if (is_absolute) { 233 qemu_spice_add_interface(&pointer->tablet.base); 234 } else { 235 spice_server_remove_interface(&pointer->tablet.base); 236 } 237 pointer->absolute = is_absolute; 238 } 239 240 void qemu_spice_input_init(void) 241 { 242 QemuSpiceKbd *kbd; 243 QemuSpicePointer *pointer; 244 245 kbd = g_malloc0(sizeof(*kbd)); 246 kbd->sin.base.sif = &kbd_interface.base; 247 qemu_spice_add_interface(&kbd->sin.base); 248 qemu_add_led_event_handler(kbd_leds, kbd); 249 250 pointer = g_malloc0(sizeof(*pointer)); 251 pointer->mouse.base.sif = &mouse_interface.base; 252 pointer->tablet.base.sif = &tablet_interface.base; 253 qemu_spice_add_interface(&pointer->mouse.base); 254 255 pointer->absolute = false; 256 pointer->mouse_mode.notify = mouse_mode_notifier; 257 qemu_add_mouse_mode_change_notifier(&pointer->mouse_mode); 258 mouse_mode_notifier(&pointer->mouse_mode, NULL); 259 } 260