blob: 610ddb00a42c63fad8276f35a353be02288d9fe2 [file] [log] [blame]
Dave Airlie47c03742013-12-10 14:05:51 +10001/*
2 * QEMU SDL display driver
3 *
4 * Copyright (c) 2003 Fabrice Bellard
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24/* Ported SDL 1.2 code to 2.0 by Dave Airlie. */
25
Dave Airlie47c03742013-12-10 14:05:51 +100026#include "qemu-common.h"
27#include "ui/console.h"
28#include "ui/input.h"
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +010029#include "ui/sdl2.h"
Dave Airlie47c03742013-12-10 14:05:51 +100030#include "sysemu/sysemu.h"
Dave Airlie47c03742013-12-10 14:05:51 +100031
Dave Airlie47c03742013-12-10 14:05:51 +100032static int sdl2_num_outputs;
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +010033static struct sdl2_console *sdl2_console;
Dave Airlie47c03742013-12-10 14:05:51 +100034
35static SDL_Surface *guest_sprite_surface;
36static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
37
Dave Airlie47c03742013-12-10 14:05:51 +100038static int gui_saved_grab;
39static int gui_fullscreen;
40static int gui_noframe;
41static int gui_key_modifier_pressed;
42static int gui_keysym;
43static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
Dave Airlie47c03742013-12-10 14:05:51 +100044static SDL_Cursor *sdl_cursor_normal;
45static SDL_Cursor *sdl_cursor_hidden;
46static int absolute_enabled;
47static int guest_cursor;
48static int guest_x, guest_y;
49static SDL_Cursor *guest_sprite;
Dave Airlie47c03742013-12-10 14:05:51 +100050static Notifier mouse_mode_notifier;
51
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +010052static void sdl_update_caption(struct sdl2_console *scon);
Dave Airlie47c03742013-12-10 14:05:51 +100053
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +010054static struct sdl2_console *get_scon_from_window(uint32_t window_id)
Dave Airlie47c03742013-12-10 14:05:51 +100055{
56 int i;
57 for (i = 0; i < sdl2_num_outputs; i++) {
58 if (sdl2_console[i].real_window == SDL_GetWindowFromID(window_id)) {
59 return &sdl2_console[i];
60 }
61 }
62 return NULL;
63}
64
Gerd Hoffmann2c3056f2014-11-11 13:22:49 +010065void sdl2_window_create(struct sdl2_console *scon)
Dave Airlie47c03742013-12-10 14:05:51 +100066{
Gerd Hoffmann46522a82014-11-11 13:12:02 +010067 int flags = 0;
Dave Airlie47c03742013-12-10 14:05:51 +100068
Gerd Hoffmann46522a82014-11-11 13:12:02 +010069 if (!scon->surface) {
70 return;
Dave Airlie47c03742013-12-10 14:05:51 +100071 }
Gerd Hoffmann46522a82014-11-11 13:12:02 +010072 assert(!scon->real_window);
73
74 if (gui_fullscreen) {
75 flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
76 } else {
77 flags |= SDL_WINDOW_RESIZABLE;
78 }
79 if (scon->hidden) {
80 flags |= SDL_WINDOW_HIDDEN;
81 }
82
83 scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED,
84 SDL_WINDOWPOS_UNDEFINED,
85 surface_width(scon->surface),
86 surface_height(scon->surface),
87 flags);
88 scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0);
89 sdl_update_caption(scon);
90}
91
Gerd Hoffmann2c3056f2014-11-11 13:22:49 +010092void sdl2_window_destroy(struct sdl2_console *scon)
Gerd Hoffmann46522a82014-11-11 13:12:02 +010093{
94 if (!scon->real_window) {
95 return;
96 }
97
98 SDL_DestroyRenderer(scon->real_renderer);
99 scon->real_renderer = NULL;
100 SDL_DestroyWindow(scon->real_window);
101 scon->real_window = NULL;
102}
103
Gerd Hoffmann2c3056f2014-11-11 13:22:49 +0100104void sdl2_window_resize(struct sdl2_console *scon)
Gerd Hoffmann46522a82014-11-11 13:12:02 +0100105{
106 if (!scon->real_window) {
107 return;
108 }
109
110 SDL_SetWindowSize(scon->real_window,
111 surface_width(scon->surface),
112 surface_height(scon->surface));
Dave Airlie47c03742013-12-10 14:05:51 +1000113}
114
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100115static void sdl_update_caption(struct sdl2_console *scon)
Dave Airlie47c03742013-12-10 14:05:51 +1000116{
117 char win_title[1024];
118 char icon_title[1024];
119 const char *status = "";
120
121 if (!runstate_is_running()) {
122 status = " [Stopped]";
123 } else if (gui_grab) {
124 if (alt_grab) {
Gerd Hoffmann44f017d2014-11-11 12:02:50 +0100125 status = " - Press Ctrl-Alt-Shift to exit grab";
Dave Airlie47c03742013-12-10 14:05:51 +1000126 } else if (ctrl_grab) {
Gerd Hoffmann44f017d2014-11-11 12:02:50 +0100127 status = " - Press Right-Ctrl to exit grab";
Dave Airlie47c03742013-12-10 14:05:51 +1000128 } else {
Gerd Hoffmann44f017d2014-11-11 12:02:50 +0100129 status = " - Press Ctrl-Alt to exit grab";
Dave Airlie47c03742013-12-10 14:05:51 +1000130 }
131 }
132
133 if (qemu_name) {
134 snprintf(win_title, sizeof(win_title), "QEMU (%s-%d)%s", qemu_name,
135 scon->idx, status);
136 snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name);
137 } else {
138 snprintf(win_title, sizeof(win_title), "QEMU%s", status);
139 snprintf(icon_title, sizeof(icon_title), "QEMU");
140 }
141
142 if (scon->real_window) {
143 SDL_SetWindowTitle(scon->real_window, win_title);
144 }
145}
146
147static void sdl_hide_cursor(void)
148{
149 if (!cursor_hide) {
150 return;
151 }
152
153 if (qemu_input_is_absolute()) {
154 SDL_ShowCursor(1);
155 SDL_SetCursor(sdl_cursor_hidden);
156 } else {
Cole Robinson2d968ff2014-04-01 16:37:11 -0400157 SDL_SetRelativeMouseMode(SDL_TRUE);
Dave Airlie47c03742013-12-10 14:05:51 +1000158 }
159}
160
161static void sdl_show_cursor(void)
162{
163 if (!cursor_hide) {
164 return;
165 }
166
167 if (!qemu_input_is_absolute()) {
Cole Robinson2d968ff2014-04-01 16:37:11 -0400168 SDL_SetRelativeMouseMode(SDL_FALSE);
Dave Airlie47c03742013-12-10 14:05:51 +1000169 SDL_ShowCursor(1);
170 if (guest_cursor &&
171 (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
172 SDL_SetCursor(guest_sprite);
173 } else {
174 SDL_SetCursor(sdl_cursor_normal);
175 }
176 }
177}
178
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100179static void sdl_grab_start(struct sdl2_console *scon)
Dave Airlie47c03742013-12-10 14:05:51 +1000180{
Gerd Hoffmannf2335792014-05-26 14:05:51 +0200181 QemuConsole *con = scon ? scon->dcl.con : NULL;
182
183 if (!con || !qemu_console_is_graphic(con)) {
184 return;
185 }
Dave Airlie47c03742013-12-10 14:05:51 +1000186 /*
187 * If the application is not active, do not try to enter grab state. This
188 * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the
189 * application (SDL bug).
190 */
191 if (!(SDL_GetWindowFlags(scon->real_window) & SDL_WINDOW_INPUT_FOCUS)) {
192 return;
193 }
194 if (guest_cursor) {
195 SDL_SetCursor(guest_sprite);
196 if (!qemu_input_is_absolute() && !absolute_enabled) {
197 SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y);
198 }
199 } else {
200 sdl_hide_cursor();
201 }
202 SDL_SetWindowGrab(scon->real_window, SDL_TRUE);
203 gui_grab = 1;
204 sdl_update_caption(scon);
205}
206
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100207static void sdl_grab_end(struct sdl2_console *scon)
Dave Airlie47c03742013-12-10 14:05:51 +1000208{
209 SDL_SetWindowGrab(scon->real_window, SDL_FALSE);
210 gui_grab = 0;
211 sdl_show_cursor();
212 sdl_update_caption(scon);
213}
214
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100215static void absolute_mouse_grab(struct sdl2_console *scon)
Dave Airlie47c03742013-12-10 14:05:51 +1000216{
217 int mouse_x, mouse_y;
218 int scr_w, scr_h;
219 SDL_GetMouseState(&mouse_x, &mouse_y);
220 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
221 if (mouse_x > 0 && mouse_x < scr_w - 1 &&
222 mouse_y > 0 && mouse_y < scr_h - 1) {
223 sdl_grab_start(scon);
224 }
225}
226
227static void sdl_mouse_mode_change(Notifier *notify, void *data)
228{
229 if (qemu_input_is_absolute()) {
230 if (!absolute_enabled) {
231 absolute_enabled = 1;
232 absolute_mouse_grab(&sdl2_console[0]);
233 }
234 } else if (absolute_enabled) {
235 if (!gui_fullscreen) {
236 sdl_grab_end(&sdl2_console[0]);
237 }
238 absolute_enabled = 0;
239 }
240}
241
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100242static void sdl_send_mouse_event(struct sdl2_console *scon, int dx, int dy,
Cole Robinson3f2fde22014-04-21 18:58:50 -0400243 int x, int y, int state)
Dave Airlie47c03742013-12-10 14:05:51 +1000244{
245 static uint32_t bmap[INPUT_BUTTON_MAX] = {
246 [INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT),
247 [INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE),
248 [INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT),
Dave Airlie47c03742013-12-10 14:05:51 +1000249 };
250 static uint32_t prev_state;
251
252 if (prev_state != state) {
253 qemu_input_update_buttons(scon->dcl.con, bmap, prev_state, state);
254 prev_state = state;
255 }
256
257 if (qemu_input_is_absolute()) {
258 int scr_w, scr_h;
259 int max_w = 0, max_h = 0;
260 int off_x = 0, off_y = 0;
261 int cur_off_x = 0, cur_off_y = 0;
262 int i;
263
264 for (i = 0; i < sdl2_num_outputs; i++) {
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100265 struct sdl2_console *thiscon = &sdl2_console[i];
Dave Airlie47c03742013-12-10 14:05:51 +1000266 if (thiscon->real_window && thiscon->surface) {
267 SDL_GetWindowSize(thiscon->real_window, &scr_w, &scr_h);
268 cur_off_x = thiscon->x;
269 cur_off_y = thiscon->y;
270 if (scr_w + cur_off_x > max_w) {
271 max_w = scr_w + cur_off_x;
272 }
273 if (scr_h + cur_off_y > max_h) {
274 max_h = scr_h + cur_off_y;
275 }
276 if (i == scon->idx) {
277 off_x = cur_off_x;
278 off_y = cur_off_y;
279 }
280 }
281 }
282 qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, max_w);
283 qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, off_y + y, max_h);
Cole Robinsonafbc0dd2014-04-01 16:37:10 -0400284 } else {
285 if (guest_cursor) {
286 x -= guest_x;
287 y -= guest_y;
288 guest_x += x;
289 guest_y += y;
290 dx = x;
291 dy = y;
292 }
293 qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_X, dx);
294 qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_Y, dy);
Dave Airlie47c03742013-12-10 14:05:51 +1000295 }
296 qemu_input_event_sync();
297}
298
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100299static void toggle_full_screen(struct sdl2_console *scon)
Dave Airlie47c03742013-12-10 14:05:51 +1000300{
Dave Airlie47c03742013-12-10 14:05:51 +1000301 gui_fullscreen = !gui_fullscreen;
302 if (gui_fullscreen) {
Gerd Hoffmann46522a82014-11-11 13:12:02 +0100303 SDL_SetWindowFullscreen(scon->real_window,
304 SDL_WINDOW_FULLSCREEN_DESKTOP);
Dave Airlie47c03742013-12-10 14:05:51 +1000305 gui_saved_grab = gui_grab;
306 sdl_grab_start(scon);
307 } else {
Dave Airlie47c03742013-12-10 14:05:51 +1000308 if (!gui_saved_grab) {
309 sdl_grab_end(scon);
310 }
Gerd Hoffmann46522a82014-11-11 13:12:02 +0100311 SDL_SetWindowFullscreen(scon->real_window, 0);
Dave Airlie47c03742013-12-10 14:05:51 +1000312 }
Gerd Hoffmann0d01b7c2014-11-11 13:31:08 +0100313 sdl2_2d_redraw(scon);
Dave Airlie47c03742013-12-10 14:05:51 +1000314}
315
316static void handle_keydown(SDL_Event *ev)
317{
Gerd Hoffmann363f59d2014-05-27 09:44:39 +0200318 int mod_state, win;
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100319 struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
Dave Airlie47c03742013-12-10 14:05:51 +1000320
321 if (alt_grab) {
322 mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) ==
323 (gui_grab_code | KMOD_LSHIFT);
324 } else if (ctrl_grab) {
325 mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL;
326 } else {
327 mod_state = (SDL_GetModState() & gui_grab_code) == gui_grab_code;
328 }
329 gui_key_modifier_pressed = mod_state;
330
331 if (gui_key_modifier_pressed) {
332 switch (ev->key.keysym.scancode) {
Gerd Hoffmann363f59d2014-05-27 09:44:39 +0200333 case SDL_SCANCODE_2:
334 case SDL_SCANCODE_3:
335 case SDL_SCANCODE_4:
336 case SDL_SCANCODE_5:
337 case SDL_SCANCODE_6:
338 case SDL_SCANCODE_7:
339 case SDL_SCANCODE_8:
340 case SDL_SCANCODE_9:
341 win = ev->key.keysym.scancode - SDL_SCANCODE_1;
342 if (win < sdl2_num_outputs) {
343 sdl2_console[win].hidden = !sdl2_console[win].hidden;
344 if (sdl2_console[win].real_window) {
345 if (sdl2_console[win].hidden) {
346 SDL_HideWindow(sdl2_console[win].real_window);
347 } else {
348 SDL_ShowWindow(sdl2_console[win].real_window);
349 }
350 }
351 gui_keysym = 1;
352 }
353 break;
Dave Airlie47c03742013-12-10 14:05:51 +1000354 case SDL_SCANCODE_F:
355 toggle_full_screen(scon);
356 gui_keysym = 1;
357 break;
358 case SDL_SCANCODE_U:
Gerd Hoffmann46522a82014-11-11 13:12:02 +0100359 sdl2_window_destroy(scon);
360 sdl2_window_create(scon);
Gerd Hoffmann0d01b7c2014-11-11 13:31:08 +0100361 /* re-create texture */
362 sdl2_2d_switch(&scon->dcl, scon->surface);
Dave Airlie47c03742013-12-10 14:05:51 +1000363 gui_keysym = 1;
364 break;
Gerd Hoffmann46522a82014-11-11 13:12:02 +0100365#if 0
Dave Airlie47c03742013-12-10 14:05:51 +1000366 case SDL_SCANCODE_KP_PLUS:
367 case SDL_SCANCODE_KP_MINUS:
368 if (!gui_fullscreen) {
369 int scr_w, scr_h;
370 int width, height;
371 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
372
373 width = MAX(scr_w + (ev->key.keysym.scancode ==
374 SDL_SCANCODE_KP_PLUS ? 50 : -50),
375 160);
376 height = (surface_height(scon->surface) * width) /
377 surface_width(scon->surface);
Gerd Hoffmann46522a82014-11-11 13:12:02 +0100378 fprintf(stderr, "%s: scale to %dx%d\n",
379 __func__, width, height);
Dave Airlie47c03742013-12-10 14:05:51 +1000380 sdl_scale(scon, width, height);
Gerd Hoffmann0d01b7c2014-11-11 13:31:08 +0100381 sdl2_2d_redraw(scon);
Dave Airlie47c03742013-12-10 14:05:51 +1000382 gui_keysym = 1;
383 }
Gerd Hoffmann46522a82014-11-11 13:12:02 +0100384#endif
Dave Airlie47c03742013-12-10 14:05:51 +1000385 default:
386 break;
387 }
388 }
389 if (!gui_keysym) {
Gerd Hoffmann8fc1a3f2014-11-11 10:58:19 +0100390 sdl2_process_key(scon, &ev->key);
Dave Airlie47c03742013-12-10 14:05:51 +1000391 }
392}
393
394static void handle_keyup(SDL_Event *ev)
395{
396 int mod_state;
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100397 struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
Dave Airlie47c03742013-12-10 14:05:51 +1000398
399 if (!alt_grab) {
400 mod_state = (ev->key.keysym.mod & gui_grab_code);
401 } else {
402 mod_state = (ev->key.keysym.mod & (gui_grab_code | KMOD_LSHIFT));
403 }
404 if (!mod_state && gui_key_modifier_pressed) {
405 gui_key_modifier_pressed = 0;
406 if (gui_keysym == 0) {
407 /* exit/enter grab if pressing Ctrl-Alt */
408 if (!gui_grab) {
409 sdl_grab_start(scon);
410 } else if (!gui_fullscreen) {
411 sdl_grab_end(scon);
412 }
413 /* SDL does not send back all the modifiers key, so we must
414 * correct it. */
Gerd Hoffmann8fc1a3f2014-11-11 10:58:19 +0100415 sdl2_reset_keys(scon);
Dave Airlie47c03742013-12-10 14:05:51 +1000416 return;
417 }
418 gui_keysym = 0;
419 }
420 if (!gui_keysym) {
Gerd Hoffmann8fc1a3f2014-11-11 10:58:19 +0100421 sdl2_process_key(scon, &ev->key);
Dave Airlie47c03742013-12-10 14:05:51 +1000422 }
423}
424
Gerd Hoffmannf2335792014-05-26 14:05:51 +0200425static void handle_textinput(SDL_Event *ev)
426{
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100427 struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
Gerd Hoffmannf2335792014-05-26 14:05:51 +0200428 QemuConsole *con = scon ? scon->dcl.con : NULL;
429
430 if (qemu_console_is_graphic(con)) {
431 return;
432 }
433 kbd_put_string_console(con, ev->text.text, strlen(ev->text.text));
434}
435
Dave Airlie47c03742013-12-10 14:05:51 +1000436static void handle_mousemotion(SDL_Event *ev)
437{
438 int max_x, max_y;
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100439 struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
Dave Airlie47c03742013-12-10 14:05:51 +1000440
441 if (qemu_input_is_absolute() || absolute_enabled) {
442 int scr_w, scr_h;
443 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
444 max_x = scr_w - 1;
445 max_y = scr_h - 1;
446 if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 ||
447 ev->motion.x == max_x || ev->motion.y == max_y)) {
448 sdl_grab_end(scon);
449 }
450 if (!gui_grab &&
451 (ev->motion.x > 0 && ev->motion.x < max_x &&
452 ev->motion.y > 0 && ev->motion.y < max_y)) {
453 sdl_grab_start(scon);
454 }
455 }
456 if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
Cole Robinson3f2fde22014-04-21 18:58:50 -0400457 sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel,
Dave Airlie47c03742013-12-10 14:05:51 +1000458 ev->motion.x, ev->motion.y, ev->motion.state);
459 }
460}
461
462static void handle_mousebutton(SDL_Event *ev)
463{
464 int buttonstate = SDL_GetMouseState(NULL, NULL);
465 SDL_MouseButtonEvent *bev;
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100466 struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
Dave Airlie47c03742013-12-10 14:05:51 +1000467
468 bev = &ev->button;
469 if (!gui_grab && !qemu_input_is_absolute()) {
470 if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
471 /* start grabbing all events */
472 sdl_grab_start(scon);
473 }
474 } else {
Dave Airlie47c03742013-12-10 14:05:51 +1000475 if (ev->type == SDL_MOUSEBUTTONDOWN) {
476 buttonstate |= SDL_BUTTON(bev->button);
477 } else {
478 buttonstate &= ~SDL_BUTTON(bev->button);
479 }
Cole Robinson3f2fde22014-04-21 18:58:50 -0400480 sdl_send_mouse_event(scon, 0, 0, bev->x, bev->y, buttonstate);
Dave Airlie47c03742013-12-10 14:05:51 +1000481 }
482}
483
Cole Robinson3f2fde22014-04-21 18:58:50 -0400484static void handle_mousewheel(SDL_Event *ev)
485{
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100486 struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
Cole Robinson3f2fde22014-04-21 18:58:50 -0400487 SDL_MouseWheelEvent *wev = &ev->wheel;
488 InputButton btn;
489
490 if (wev->y > 0) {
491 btn = INPUT_BUTTON_WHEEL_UP;
492 } else if (wev->y < 0) {
493 btn = INPUT_BUTTON_WHEEL_DOWN;
494 } else {
495 return;
496 }
497
498 qemu_input_queue_btn(scon->dcl.con, btn, true);
499 qemu_input_event_sync();
500 qemu_input_queue_btn(scon->dcl.con, btn, false);
501 qemu_input_event_sync();
502}
503
Max Reitz1dfc5c82014-12-12 10:52:51 +0100504static void handle_windowevent(SDL_Event *ev)
Dave Airlie47c03742013-12-10 14:05:51 +1000505{
Max Reitz1dfc5c82014-12-12 10:52:51 +0100506 struct sdl2_console *scon = get_scon_from_window(ev->window.windowID);
507
Dave Airlie47c03742013-12-10 14:05:51 +1000508 switch (ev->window.event) {
509 case SDL_WINDOWEVENT_RESIZED:
Dave Airlie8b15d9f2014-03-25 16:50:36 +1000510 {
511 QemuUIInfo info;
512 memset(&info, 0, sizeof(info));
513 info.width = ev->window.data1;
514 info.height = ev->window.data2;
515 dpy_set_ui_info(scon->dcl.con, &info);
516 }
Gerd Hoffmann0d01b7c2014-11-11 13:31:08 +0100517 sdl2_2d_redraw(scon);
Dave Airlie47c03742013-12-10 14:05:51 +1000518 break;
519 case SDL_WINDOWEVENT_EXPOSED:
Gerd Hoffmann0d01b7c2014-11-11 13:31:08 +0100520 sdl2_2d_redraw(scon);
Dave Airlie47c03742013-12-10 14:05:51 +1000521 break;
522 case SDL_WINDOWEVENT_FOCUS_GAINED:
523 case SDL_WINDOWEVENT_ENTER:
524 if (!gui_grab && (qemu_input_is_absolute() || absolute_enabled)) {
525 absolute_mouse_grab(scon);
526 }
527 break;
528 case SDL_WINDOWEVENT_FOCUS_LOST:
529 if (gui_grab && !gui_fullscreen) {
530 sdl_grab_end(scon);
531 }
532 break;
533 case SDL_WINDOWEVENT_RESTORED:
Gerd Hoffmann63ed4902014-11-12 08:01:27 +0100534 update_displaychangelistener(&scon->dcl, GUI_REFRESH_INTERVAL_DEFAULT);
Dave Airlie47c03742013-12-10 14:05:51 +1000535 break;
536 case SDL_WINDOWEVENT_MINIMIZED:
Gerd Hoffmann63ed4902014-11-12 08:01:27 +0100537 update_displaychangelistener(&scon->dcl, 500);
Dave Airlie47c03742013-12-10 14:05:51 +1000538 break;
539 case SDL_WINDOWEVENT_CLOSE:
540 if (!no_quit) {
541 no_shutdown = 0;
542 qemu_system_shutdown_request();
543 }
544 break;
Max Reitzd3f3a0f2014-12-12 10:52:52 +0100545 case SDL_WINDOWEVENT_SHOWN:
546 if (scon->hidden) {
547 SDL_HideWindow(scon->real_window);
548 }
549 break;
550 case SDL_WINDOWEVENT_HIDDEN:
551 if (!scon->hidden) {
552 SDL_ShowWindow(scon->real_window);
553 }
554 break;
Dave Airlie47c03742013-12-10 14:05:51 +1000555 }
556}
557
Gerd Hoffmann63ed4902014-11-12 08:01:27 +0100558void sdl2_poll_events(struct sdl2_console *scon)
Dave Airlie47c03742013-12-10 14:05:51 +1000559{
Dave Airlie47c03742013-12-10 14:05:51 +1000560 SDL_Event ev1, *ev = &ev1;
561
562 if (scon->last_vm_running != runstate_is_running()) {
563 scon->last_vm_running = runstate_is_running();
564 sdl_update_caption(scon);
565 }
566
Dave Airlie47c03742013-12-10 14:05:51 +1000567 while (SDL_PollEvent(ev)) {
568 switch (ev->type) {
569 case SDL_KEYDOWN:
570 handle_keydown(ev);
571 break;
572 case SDL_KEYUP:
573 handle_keyup(ev);
574 break;
Gerd Hoffmannf2335792014-05-26 14:05:51 +0200575 case SDL_TEXTINPUT:
576 handle_textinput(ev);
577 break;
Dave Airlie47c03742013-12-10 14:05:51 +1000578 case SDL_QUIT:
579 if (!no_quit) {
580 no_shutdown = 0;
581 qemu_system_shutdown_request();
582 }
583 break;
584 case SDL_MOUSEMOTION:
585 handle_mousemotion(ev);
586 break;
587 case SDL_MOUSEBUTTONDOWN:
588 case SDL_MOUSEBUTTONUP:
589 handle_mousebutton(ev);
590 break;
Cole Robinson3f2fde22014-04-21 18:58:50 -0400591 case SDL_MOUSEWHEEL:
592 handle_mousewheel(ev);
593 break;
Dave Airlie47c03742013-12-10 14:05:51 +1000594 case SDL_WINDOWEVENT:
Max Reitz1dfc5c82014-12-12 10:52:51 +0100595 handle_windowevent(ev);
Dave Airlie47c03742013-12-10 14:05:51 +1000596 break;
597 default:
598 break;
599 }
600 }
601}
602
603static void sdl_mouse_warp(DisplayChangeListener *dcl,
604 int x, int y, int on)
605{
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100606 struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
Dave Airlie47c03742013-12-10 14:05:51 +1000607 if (on) {
608 if (!guest_cursor) {
609 sdl_show_cursor();
610 }
611 if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
612 SDL_SetCursor(guest_sprite);
613 if (!qemu_input_is_absolute() && !absolute_enabled) {
614 SDL_WarpMouseInWindow(scon->real_window, x, y);
615 }
616 }
617 } else if (gui_grab) {
618 sdl_hide_cursor();
619 }
620 guest_cursor = on;
621 guest_x = x, guest_y = y;
622}
623
624static void sdl_mouse_define(DisplayChangeListener *dcl,
625 QEMUCursor *c)
626{
627
628 if (guest_sprite) {
629 SDL_FreeCursor(guest_sprite);
630 }
631
632 if (guest_sprite_surface) {
633 SDL_FreeSurface(guest_sprite_surface);
634 }
635
636 guest_sprite_surface =
637 SDL_CreateRGBSurfaceFrom(c->data, c->width, c->height, 32, c->width * 4,
638 0xff0000, 0x00ff00, 0xff, 0xff000000);
639
640 if (!guest_sprite_surface) {
641 fprintf(stderr, "Failed to make rgb surface from %p\n", c);
642 return;
643 }
644 guest_sprite = SDL_CreateColorCursor(guest_sprite_surface,
645 c->hot_x, c->hot_y);
646 if (!guest_sprite) {
647 fprintf(stderr, "Failed to make color cursor from %p\n", c);
648 return;
649 }
650 if (guest_cursor &&
651 (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
652 SDL_SetCursor(guest_sprite);
653 }
654}
655
656static void sdl_cleanup(void)
657{
658 if (guest_sprite) {
659 SDL_FreeCursor(guest_sprite);
660 }
661 SDL_QuitSubSystem(SDL_INIT_VIDEO);
662}
663
Gerd Hoffmannf1ddebd2014-11-11 11:09:26 +0100664static const DisplayChangeListenerOps dcl_2d_ops = {
Gerd Hoffmann877417d2015-01-09 09:27:09 +0100665 .dpy_name = "sdl2-2d",
666 .dpy_gfx_update = sdl2_2d_update,
667 .dpy_gfx_switch = sdl2_2d_switch,
668 .dpy_gfx_check_format = sdl2_2d_check_format,
669 .dpy_refresh = sdl2_2d_refresh,
670 .dpy_mouse_set = sdl_mouse_warp,
671 .dpy_cursor_define = sdl_mouse_define,
Dave Airlie47c03742013-12-10 14:05:51 +1000672};
673
674void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
675{
676 int flags;
677 uint8_t data = 0;
678 char *filename;
679 int i;
680
681 if (no_frame) {
682 gui_noframe = 1;
683 }
684
685#ifdef __linux__
686 /* on Linux, SDL may use fbcon|directfb|svgalib when run without
687 * accessible $DISPLAY to open X11 window. This is often the case
688 * when qemu is run using sudo. But in this case, and when actually
689 * run in X11 environment, SDL fights with X11 for the video card,
690 * making current display unavailable, often until reboot.
691 * So make x11 the default SDL video driver if this variable is unset.
692 * This is a bit hackish but saves us from bigger problem.
693 * Maybe it's a good idea to fix this in SDL instead.
694 */
695 setenv("SDL_VIDEODRIVER", "x11", 0);
696#endif
697
698 flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
699 if (SDL_Init(flags)) {
700 fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
701 SDL_GetError());
702 exit(1);
703 }
Gerd Hoffmann44f017d2014-11-11 12:02:50 +0100704 SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "1");
Dave Airlie47c03742013-12-10 14:05:51 +1000705
706 for (i = 0;; i++) {
707 QemuConsole *con = qemu_console_lookup_by_index(i);
Gerd Hoffmannf2335792014-05-26 14:05:51 +0200708 if (!con) {
Dave Airlie47c03742013-12-10 14:05:51 +1000709 break;
710 }
711 }
712 sdl2_num_outputs = i;
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100713 sdl2_console = g_new0(struct sdl2_console, sdl2_num_outputs);
Dave Airlie47c03742013-12-10 14:05:51 +1000714 for (i = 0; i < sdl2_num_outputs; i++) {
715 QemuConsole *con = qemu_console_lookup_by_index(i);
Gerd Hoffmannf2335792014-05-26 14:05:51 +0200716 if (!qemu_console_is_graphic(con)) {
717 sdl2_console[i].hidden = true;
718 }
Gerd Hoffmannf1ddebd2014-11-11 11:09:26 +0100719 sdl2_console[i].dcl.ops = &dcl_2d_ops;
Dave Airlie47c03742013-12-10 14:05:51 +1000720 sdl2_console[i].dcl.con = con;
721 register_displaychangelistener(&sdl2_console[i].dcl);
722 sdl2_console[i].idx = i;
723 }
724
725 /* Load a 32x32x4 image. White pixels are transparent. */
726 filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp");
727 if (filename) {
728 SDL_Surface *image = SDL_LoadBMP(filename);
729 if (image) {
730 uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255);
731 SDL_SetColorKey(image, SDL_TRUE, colorkey);
732 SDL_SetWindowIcon(sdl2_console[0].real_window, image);
733 }
734 g_free(filename);
735 }
736
737 if (full_screen) {
738 gui_fullscreen = 1;
739 sdl_grab_start(0);
740 }
741
742 mouse_mode_notifier.notify = sdl_mouse_mode_change;
743 qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);
744
745 gui_grab = 0;
746
747 sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
748 sdl_cursor_normal = SDL_GetCursor();
749
750 atexit(sdl_cleanup);
751}