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