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