blob: 749fa89523500bc4a34fc299254dfcb378a6c90e [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
26/* Avoid compiler warning because macro is redefined in SDL_syswm.h. */
27#undef WIN32_LEAN_AND_MEAN
28
29#include <SDL.h>
30
31#if SDL_MAJOR_VERSION == 2
32#include <SDL_syswm.h>
33
34#include "qemu-common.h"
35#include "ui/console.h"
36#include "ui/input.h"
37#include "sysemu/sysemu.h"
38#include "sdl_zoom.h"
39
40#include "sdl2-keymap.h"
41
42static int sdl2_num_outputs;
43static struct sdl2_state {
44 DisplayChangeListener dcl;
45 DisplaySurface *surface;
46 SDL_Texture *texture;
47 SDL_Window *real_window;
48 SDL_Renderer *real_renderer;
49 int idx;
50 int last_vm_running; /* per console for caption reasons */
51 int x, y;
Gerd Hoffmann363f59d2014-05-27 09:44:39 +020052 int hidden;
Dave Airlie47c03742013-12-10 14:05:51 +100053} *sdl2_console;
54
55static SDL_Surface *guest_sprite_surface;
56static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
57
58static bool gui_saved_scaling;
59static int gui_saved_width;
60static int gui_saved_height;
61static int gui_saved_grab;
62static int gui_fullscreen;
63static int gui_noframe;
64static int gui_key_modifier_pressed;
65static int gui_keysym;
66static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
67static uint8_t modifiers_state[SDL_NUM_SCANCODES];
68static SDL_Cursor *sdl_cursor_normal;
69static SDL_Cursor *sdl_cursor_hidden;
70static int absolute_enabled;
71static int guest_cursor;
72static int guest_x, guest_y;
73static SDL_Cursor *guest_sprite;
74static int scaling_active;
75static Notifier mouse_mode_notifier;
76
77static void sdl_update_caption(struct sdl2_state *scon);
78
79static struct sdl2_state *get_scon_from_window(uint32_t window_id)
80{
81 int i;
82 for (i = 0; i < sdl2_num_outputs; i++) {
83 if (sdl2_console[i].real_window == SDL_GetWindowFromID(window_id)) {
84 return &sdl2_console[i];
85 }
86 }
87 return NULL;
88}
89
90static void sdl_update(DisplayChangeListener *dcl,
91 int x, int y, int w, int h)
92{
93 struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
94 SDL_Rect rect;
95 DisplaySurface *surf = qemu_console_surface(dcl->con);
96
97 if (!surf) {
98 return;
99 }
100 if (!scon->texture) {
101 return;
102 }
103
104 rect.x = x;
105 rect.y = y;
106 rect.w = w;
107 rect.h = h;
108
109 SDL_UpdateTexture(scon->texture, NULL, surface_data(surf),
110 surface_stride(surf));
111 SDL_RenderCopy(scon->real_renderer, scon->texture, &rect, &rect);
112 SDL_RenderPresent(scon->real_renderer);
113}
114
115static void do_sdl_resize(struct sdl2_state *scon, int width, int height,
116 int bpp)
117{
118 int flags;
119
120 if (scon->real_window && scon->real_renderer) {
121 if (width && height) {
122 SDL_RenderSetLogicalSize(scon->real_renderer, width, height);
123 SDL_SetWindowSize(scon->real_window, width, height);
124 } else {
125 SDL_DestroyRenderer(scon->real_renderer);
126 SDL_DestroyWindow(scon->real_window);
127 scon->real_renderer = NULL;
128 scon->real_window = NULL;
129 }
130 } else {
131 if (!width || !height) {
132 return;
133 }
134 flags = 0;
135 if (gui_fullscreen) {
136 flags |= SDL_WINDOW_FULLSCREEN;
137 } else {
138 flags |= SDL_WINDOW_RESIZABLE;
139 }
Gerd Hoffmann363f59d2014-05-27 09:44:39 +0200140 if (scon->hidden) {
141 flags |= SDL_WINDOW_HIDDEN;
142 }
Dave Airlie47c03742013-12-10 14:05:51 +1000143
144 scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED,
145 SDL_WINDOWPOS_UNDEFINED,
146 width, height, flags);
147 scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0);
148 sdl_update_caption(scon);
149 }
150}
151
152static void sdl_switch(DisplayChangeListener *dcl,
153 DisplaySurface *new_surface)
154{
155 struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
156 int format = 0;
157 int idx = scon->idx;
158 DisplaySurface *old_surface = scon->surface;
159
160 /* temporary hack: allows to call sdl_switch to handle scaling changes */
161 if (new_surface) {
162 scon->surface = new_surface;
163 }
164
165 if (!new_surface && idx > 0) {
166 scon->surface = NULL;
167 }
168
169 if (new_surface == NULL) {
170 do_sdl_resize(scon, 0, 0, 0);
171 } else {
172 do_sdl_resize(scon, surface_width(scon->surface),
173 surface_height(scon->surface), 0);
174 }
175
176 if (old_surface && scon->texture) {
177 SDL_DestroyTexture(scon->texture);
178 scon->texture = NULL;
179 }
180
181 if (new_surface) {
182 if (!scon->texture) {
183 if (surface_bits_per_pixel(scon->surface) == 16) {
184 format = SDL_PIXELFORMAT_RGB565;
185 } else if (surface_bits_per_pixel(scon->surface) == 32) {
186 format = SDL_PIXELFORMAT_ARGB8888;
187 }
188
189 scon->texture = SDL_CreateTexture(scon->real_renderer, format,
190 SDL_TEXTUREACCESS_STREAMING,
191 surface_width(new_surface),
192 surface_height(new_surface));
193 }
194 }
195}
196
Gerd Hoffmannee8c0b62014-05-20 08:11:25 +0200197static void reset_keys(struct sdl2_state *scon)
Dave Airlie47c03742013-12-10 14:05:51 +1000198{
Gerd Hoffmannee8c0b62014-05-20 08:11:25 +0200199 QemuConsole *con = scon ? scon->dcl.con : NULL;
Dave Airlie47c03742013-12-10 14:05:51 +1000200 int i;
201
202 for (i = 0; i < 256; i++) {
203 if (modifiers_state[i]) {
204 int qcode = sdl2_scancode_to_qcode[i];
Gerd Hoffmannee8c0b62014-05-20 08:11:25 +0200205 qemu_input_event_send_key_qcode(con, qcode, false);
Dave Airlie47c03742013-12-10 14:05:51 +1000206 modifiers_state[i] = 0;
207 }
208 }
209}
210
Gerd Hoffmannee8c0b62014-05-20 08:11:25 +0200211static void sdl_process_key(struct sdl2_state *scon,
212 SDL_KeyboardEvent *ev)
Dave Airlie47c03742013-12-10 14:05:51 +1000213{
214 int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode];
Gerd Hoffmannee8c0b62014-05-20 08:11:25 +0200215 QemuConsole *con = scon ? scon->dcl.con : NULL;
Dave Airlie47c03742013-12-10 14:05:51 +1000216
217 switch (ev->keysym.scancode) {
218#if 0
219 case SDL_SCANCODE_NUMLOCKCLEAR:
220 case SDL_SCANCODE_CAPSLOCK:
221 /* SDL does not send the key up event, so we generate it */
Gerd Hoffmannee8c0b62014-05-20 08:11:25 +0200222 qemu_input_event_send_key_qcode(con, qcode, true);
223 qemu_input_event_send_key_qcode(con, qcode, false);
Dave Airlie47c03742013-12-10 14:05:51 +1000224 return;
225#endif
226 case SDL_SCANCODE_LCTRL:
227 case SDL_SCANCODE_LSHIFT:
228 case SDL_SCANCODE_LALT:
229 case SDL_SCANCODE_LGUI:
230 case SDL_SCANCODE_RCTRL:
231 case SDL_SCANCODE_RSHIFT:
232 case SDL_SCANCODE_RALT:
233 case SDL_SCANCODE_RGUI:
234 if (ev->type == SDL_KEYUP) {
235 modifiers_state[ev->keysym.scancode] = 0;
236 } else {
237 modifiers_state[ev->keysym.scancode] = 1;
238 }
239 /* fall though */
240 default:
Gerd Hoffmannee8c0b62014-05-20 08:11:25 +0200241 qemu_input_event_send_key_qcode(con, qcode,
Dave Airlie47c03742013-12-10 14:05:51 +1000242 ev->type == SDL_KEYDOWN);
243 }
244}
245
246static void sdl_update_caption(struct sdl2_state *scon)
247{
248 char win_title[1024];
249 char icon_title[1024];
250 const char *status = "";
251
252 if (!runstate_is_running()) {
253 status = " [Stopped]";
254 } else if (gui_grab) {
255 if (alt_grab) {
256 status = " - Press Ctrl-Alt-Shift to exit mouse grab";
257 } else if (ctrl_grab) {
258 status = " - Press Right-Ctrl to exit mouse grab";
259 } else {
260 status = " - Press Ctrl-Alt to exit mouse grab";
261 }
262 }
263
264 if (qemu_name) {
265 snprintf(win_title, sizeof(win_title), "QEMU (%s-%d)%s", qemu_name,
266 scon->idx, status);
267 snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name);
268 } else {
269 snprintf(win_title, sizeof(win_title), "QEMU%s", status);
270 snprintf(icon_title, sizeof(icon_title), "QEMU");
271 }
272
273 if (scon->real_window) {
274 SDL_SetWindowTitle(scon->real_window, win_title);
275 }
276}
277
278static void sdl_hide_cursor(void)
279{
280 if (!cursor_hide) {
281 return;
282 }
283
284 if (qemu_input_is_absolute()) {
285 SDL_ShowCursor(1);
286 SDL_SetCursor(sdl_cursor_hidden);
287 } else {
Cole Robinson2d968ff2014-04-01 16:37:11 -0400288 SDL_SetRelativeMouseMode(SDL_TRUE);
Dave Airlie47c03742013-12-10 14:05:51 +1000289 }
290}
291
292static void sdl_show_cursor(void)
293{
294 if (!cursor_hide) {
295 return;
296 }
297
298 if (!qemu_input_is_absolute()) {
Cole Robinson2d968ff2014-04-01 16:37:11 -0400299 SDL_SetRelativeMouseMode(SDL_FALSE);
Dave Airlie47c03742013-12-10 14:05:51 +1000300 SDL_ShowCursor(1);
301 if (guest_cursor &&
302 (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
303 SDL_SetCursor(guest_sprite);
304 } else {
305 SDL_SetCursor(sdl_cursor_normal);
306 }
307 }
308}
309
310static void sdl_grab_start(struct sdl2_state *scon)
311{
312 /*
313 * If the application is not active, do not try to enter grab state. This
314 * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the
315 * application (SDL bug).
316 */
317 if (!(SDL_GetWindowFlags(scon->real_window) & SDL_WINDOW_INPUT_FOCUS)) {
318 return;
319 }
320 if (guest_cursor) {
321 SDL_SetCursor(guest_sprite);
322 if (!qemu_input_is_absolute() && !absolute_enabled) {
323 SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y);
324 }
325 } else {
326 sdl_hide_cursor();
327 }
328 SDL_SetWindowGrab(scon->real_window, SDL_TRUE);
329 gui_grab = 1;
330 sdl_update_caption(scon);
331}
332
333static void sdl_grab_end(struct sdl2_state *scon)
334{
335 SDL_SetWindowGrab(scon->real_window, SDL_FALSE);
336 gui_grab = 0;
337 sdl_show_cursor();
338 sdl_update_caption(scon);
339}
340
341static void absolute_mouse_grab(struct sdl2_state *scon)
342{
343 int mouse_x, mouse_y;
344 int scr_w, scr_h;
345 SDL_GetMouseState(&mouse_x, &mouse_y);
346 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
347 if (mouse_x > 0 && mouse_x < scr_w - 1 &&
348 mouse_y > 0 && mouse_y < scr_h - 1) {
349 sdl_grab_start(scon);
350 }
351}
352
353static void sdl_mouse_mode_change(Notifier *notify, void *data)
354{
355 if (qemu_input_is_absolute()) {
356 if (!absolute_enabled) {
357 absolute_enabled = 1;
358 absolute_mouse_grab(&sdl2_console[0]);
359 }
360 } else if (absolute_enabled) {
361 if (!gui_fullscreen) {
362 sdl_grab_end(&sdl2_console[0]);
363 }
364 absolute_enabled = 0;
365 }
366}
367
368static void sdl_send_mouse_event(struct sdl2_state *scon, int dx, int dy,
Cole Robinson3f2fde22014-04-21 18:58:50 -0400369 int x, int y, int state)
Dave Airlie47c03742013-12-10 14:05:51 +1000370{
371 static uint32_t bmap[INPUT_BUTTON_MAX] = {
372 [INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT),
373 [INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE),
374 [INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT),
Dave Airlie47c03742013-12-10 14:05:51 +1000375 };
376 static uint32_t prev_state;
377
378 if (prev_state != state) {
379 qemu_input_update_buttons(scon->dcl.con, bmap, prev_state, state);
380 prev_state = state;
381 }
382
383 if (qemu_input_is_absolute()) {
384 int scr_w, scr_h;
385 int max_w = 0, max_h = 0;
386 int off_x = 0, off_y = 0;
387 int cur_off_x = 0, cur_off_y = 0;
388 int i;
389
390 for (i = 0; i < sdl2_num_outputs; i++) {
391 struct sdl2_state *thiscon = &sdl2_console[i];
392 if (thiscon->real_window && thiscon->surface) {
393 SDL_GetWindowSize(thiscon->real_window, &scr_w, &scr_h);
394 cur_off_x = thiscon->x;
395 cur_off_y = thiscon->y;
396 if (scr_w + cur_off_x > max_w) {
397 max_w = scr_w + cur_off_x;
398 }
399 if (scr_h + cur_off_y > max_h) {
400 max_h = scr_h + cur_off_y;
401 }
402 if (i == scon->idx) {
403 off_x = cur_off_x;
404 off_y = cur_off_y;
405 }
406 }
407 }
408 qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, max_w);
409 qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, off_y + y, max_h);
Cole Robinsonafbc0dd2014-04-01 16:37:10 -0400410 } else {
411 if (guest_cursor) {
412 x -= guest_x;
413 y -= guest_y;
414 guest_x += x;
415 guest_y += y;
416 dx = x;
417 dy = y;
418 }
419 qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_X, dx);
420 qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_Y, dy);
Dave Airlie47c03742013-12-10 14:05:51 +1000421 }
422 qemu_input_event_sync();
423}
424
425static void sdl_scale(struct sdl2_state *scon, int width, int height)
426{
427 int bpp = 0;
428 do_sdl_resize(scon, width, height, bpp);
429 scaling_active = 1;
430}
431
432static void toggle_full_screen(struct sdl2_state *scon)
433{
434 int width = surface_width(scon->surface);
435 int height = surface_height(scon->surface);
436 int bpp = surface_bits_per_pixel(scon->surface);
437
438 gui_fullscreen = !gui_fullscreen;
439 if (gui_fullscreen) {
440 SDL_GetWindowSize(scon->real_window,
441 &gui_saved_width, &gui_saved_height);
442 gui_saved_scaling = scaling_active;
443
444 do_sdl_resize(scon, width, height, bpp);
445 scaling_active = 0;
446
447 gui_saved_grab = gui_grab;
448 sdl_grab_start(scon);
449 } else {
450 if (gui_saved_scaling) {
451 sdl_scale(scon, gui_saved_width, gui_saved_height);
452 } else {
453 do_sdl_resize(scon, width, height, 0);
454 }
455 if (!gui_saved_grab) {
456 sdl_grab_end(scon);
457 }
458 }
459 graphic_hw_invalidate(scon->dcl.con);
460 graphic_hw_update(scon->dcl.con);
461}
462
463static void handle_keydown(SDL_Event *ev)
464{
Gerd Hoffmann363f59d2014-05-27 09:44:39 +0200465 int mod_state, win;
Dave Airlie47c03742013-12-10 14:05:51 +1000466 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
467
468 if (alt_grab) {
469 mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) ==
470 (gui_grab_code | KMOD_LSHIFT);
471 } else if (ctrl_grab) {
472 mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL;
473 } else {
474 mod_state = (SDL_GetModState() & gui_grab_code) == gui_grab_code;
475 }
476 gui_key_modifier_pressed = mod_state;
477
478 if (gui_key_modifier_pressed) {
479 switch (ev->key.keysym.scancode) {
Gerd Hoffmann363f59d2014-05-27 09:44:39 +0200480 case SDL_SCANCODE_2:
481 case SDL_SCANCODE_3:
482 case SDL_SCANCODE_4:
483 case SDL_SCANCODE_5:
484 case SDL_SCANCODE_6:
485 case SDL_SCANCODE_7:
486 case SDL_SCANCODE_8:
487 case SDL_SCANCODE_9:
488 win = ev->key.keysym.scancode - SDL_SCANCODE_1;
489 if (win < sdl2_num_outputs) {
490 sdl2_console[win].hidden = !sdl2_console[win].hidden;
491 if (sdl2_console[win].real_window) {
492 if (sdl2_console[win].hidden) {
493 SDL_HideWindow(sdl2_console[win].real_window);
494 } else {
495 SDL_ShowWindow(sdl2_console[win].real_window);
496 }
497 }
498 gui_keysym = 1;
499 }
500 break;
Dave Airlie47c03742013-12-10 14:05:51 +1000501 case SDL_SCANCODE_F:
502 toggle_full_screen(scon);
503 gui_keysym = 1;
504 break;
505 case SDL_SCANCODE_U:
506 if (scaling_active) {
507 scaling_active = 0;
508 sdl_switch(&scon->dcl, NULL);
509 graphic_hw_invalidate(scon->dcl.con);
510 graphic_hw_update(scon->dcl.con);
511 }
512 gui_keysym = 1;
513 break;
514 case SDL_SCANCODE_KP_PLUS:
515 case SDL_SCANCODE_KP_MINUS:
516 if (!gui_fullscreen) {
517 int scr_w, scr_h;
518 int width, height;
519 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
520
521 width = MAX(scr_w + (ev->key.keysym.scancode ==
522 SDL_SCANCODE_KP_PLUS ? 50 : -50),
523 160);
524 height = (surface_height(scon->surface) * width) /
525 surface_width(scon->surface);
526
527 sdl_scale(scon, width, height);
528 graphic_hw_invalidate(NULL);
529 graphic_hw_update(NULL);
530 gui_keysym = 1;
531 }
532 default:
533 break;
534 }
535 }
536 if (!gui_keysym) {
Gerd Hoffmannee8c0b62014-05-20 08:11:25 +0200537 sdl_process_key(scon, &ev->key);
Dave Airlie47c03742013-12-10 14:05:51 +1000538 }
539}
540
541static void handle_keyup(SDL_Event *ev)
542{
543 int mod_state;
544 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
545
546 if (!alt_grab) {
547 mod_state = (ev->key.keysym.mod & gui_grab_code);
548 } else {
549 mod_state = (ev->key.keysym.mod & (gui_grab_code | KMOD_LSHIFT));
550 }
551 if (!mod_state && gui_key_modifier_pressed) {
552 gui_key_modifier_pressed = 0;
553 if (gui_keysym == 0) {
554 /* exit/enter grab if pressing Ctrl-Alt */
555 if (!gui_grab) {
556 sdl_grab_start(scon);
557 } else if (!gui_fullscreen) {
558 sdl_grab_end(scon);
559 }
560 /* SDL does not send back all the modifiers key, so we must
561 * correct it. */
Gerd Hoffmannee8c0b62014-05-20 08:11:25 +0200562 reset_keys(scon);
Dave Airlie47c03742013-12-10 14:05:51 +1000563 return;
564 }
565 gui_keysym = 0;
566 }
567 if (!gui_keysym) {
Gerd Hoffmannee8c0b62014-05-20 08:11:25 +0200568 sdl_process_key(scon, &ev->key);
Dave Airlie47c03742013-12-10 14:05:51 +1000569 }
570}
571
572static void handle_mousemotion(SDL_Event *ev)
573{
574 int max_x, max_y;
575 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
576
577 if (qemu_input_is_absolute() || absolute_enabled) {
578 int scr_w, scr_h;
579 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
580 max_x = scr_w - 1;
581 max_y = scr_h - 1;
582 if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 ||
583 ev->motion.x == max_x || ev->motion.y == max_y)) {
584 sdl_grab_end(scon);
585 }
586 if (!gui_grab &&
587 (ev->motion.x > 0 && ev->motion.x < max_x &&
588 ev->motion.y > 0 && ev->motion.y < max_y)) {
589 sdl_grab_start(scon);
590 }
591 }
592 if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
Cole Robinson3f2fde22014-04-21 18:58:50 -0400593 sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel,
Dave Airlie47c03742013-12-10 14:05:51 +1000594 ev->motion.x, ev->motion.y, ev->motion.state);
595 }
596}
597
598static void handle_mousebutton(SDL_Event *ev)
599{
600 int buttonstate = SDL_GetMouseState(NULL, NULL);
601 SDL_MouseButtonEvent *bev;
602 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
Dave Airlie47c03742013-12-10 14:05:51 +1000603
604 bev = &ev->button;
605 if (!gui_grab && !qemu_input_is_absolute()) {
606 if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
607 /* start grabbing all events */
608 sdl_grab_start(scon);
609 }
610 } else {
Dave Airlie47c03742013-12-10 14:05:51 +1000611 if (ev->type == SDL_MOUSEBUTTONDOWN) {
612 buttonstate |= SDL_BUTTON(bev->button);
613 } else {
614 buttonstate &= ~SDL_BUTTON(bev->button);
615 }
Cole Robinson3f2fde22014-04-21 18:58:50 -0400616 sdl_send_mouse_event(scon, 0, 0, bev->x, bev->y, buttonstate);
Dave Airlie47c03742013-12-10 14:05:51 +1000617 }
618}
619
Cole Robinson3f2fde22014-04-21 18:58:50 -0400620static void handle_mousewheel(SDL_Event *ev)
621{
622 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
623 SDL_MouseWheelEvent *wev = &ev->wheel;
624 InputButton btn;
625
626 if (wev->y > 0) {
627 btn = INPUT_BUTTON_WHEEL_UP;
628 } else if (wev->y < 0) {
629 btn = INPUT_BUTTON_WHEEL_DOWN;
630 } else {
631 return;
632 }
633
634 qemu_input_queue_btn(scon->dcl.con, btn, true);
635 qemu_input_event_sync();
636 qemu_input_queue_btn(scon->dcl.con, btn, false);
637 qemu_input_event_sync();
638}
639
Dave Airlie47c03742013-12-10 14:05:51 +1000640static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev)
641{
642 int w, h;
643 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
644
645 switch (ev->window.event) {
646 case SDL_WINDOWEVENT_RESIZED:
647 sdl_scale(scon, ev->window.data1, ev->window.data2);
Dave Airlie8b15d9f2014-03-25 16:50:36 +1000648 {
649 QemuUIInfo info;
650 memset(&info, 0, sizeof(info));
651 info.width = ev->window.data1;
652 info.height = ev->window.data2;
653 dpy_set_ui_info(scon->dcl.con, &info);
654 }
Dave Airlie47c03742013-12-10 14:05:51 +1000655 graphic_hw_invalidate(scon->dcl.con);
656 graphic_hw_update(scon->dcl.con);
657 break;
658 case SDL_WINDOWEVENT_EXPOSED:
659 SDL_GetWindowSize(SDL_GetWindowFromID(ev->window.windowID), &w, &h);
660 sdl_update(dcl, 0, 0, w, h);
661 break;
662 case SDL_WINDOWEVENT_FOCUS_GAINED:
663 case SDL_WINDOWEVENT_ENTER:
664 if (!gui_grab && (qemu_input_is_absolute() || absolute_enabled)) {
665 absolute_mouse_grab(scon);
666 }
667 break;
668 case SDL_WINDOWEVENT_FOCUS_LOST:
669 if (gui_grab && !gui_fullscreen) {
670 sdl_grab_end(scon);
671 }
672 break;
673 case SDL_WINDOWEVENT_RESTORED:
674 update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT);
675 break;
676 case SDL_WINDOWEVENT_MINIMIZED:
677 update_displaychangelistener(dcl, 500);
678 break;
679 case SDL_WINDOWEVENT_CLOSE:
680 if (!no_quit) {
681 no_shutdown = 0;
682 qemu_system_shutdown_request();
683 }
684 break;
685 }
686}
687
688static void sdl_refresh(DisplayChangeListener *dcl)
689{
690 struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
691 SDL_Event ev1, *ev = &ev1;
692
693 if (scon->last_vm_running != runstate_is_running()) {
694 scon->last_vm_running = runstate_is_running();
695 sdl_update_caption(scon);
696 }
697
698 graphic_hw_update(dcl->con);
699
700 while (SDL_PollEvent(ev)) {
701 switch (ev->type) {
702 case SDL_KEYDOWN:
703 handle_keydown(ev);
704 break;
705 case SDL_KEYUP:
706 handle_keyup(ev);
707 break;
708 case SDL_QUIT:
709 if (!no_quit) {
710 no_shutdown = 0;
711 qemu_system_shutdown_request();
712 }
713 break;
714 case SDL_MOUSEMOTION:
715 handle_mousemotion(ev);
716 break;
717 case SDL_MOUSEBUTTONDOWN:
718 case SDL_MOUSEBUTTONUP:
719 handle_mousebutton(ev);
720 break;
Cole Robinson3f2fde22014-04-21 18:58:50 -0400721 case SDL_MOUSEWHEEL:
722 handle_mousewheel(ev);
723 break;
Dave Airlie47c03742013-12-10 14:05:51 +1000724 case SDL_WINDOWEVENT:
725 handle_windowevent(dcl, ev);
726 break;
727 default:
728 break;
729 }
730 }
731}
732
733static void sdl_mouse_warp(DisplayChangeListener *dcl,
734 int x, int y, int on)
735{
736 struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
737 if (on) {
738 if (!guest_cursor) {
739 sdl_show_cursor();
740 }
741 if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
742 SDL_SetCursor(guest_sprite);
743 if (!qemu_input_is_absolute() && !absolute_enabled) {
744 SDL_WarpMouseInWindow(scon->real_window, x, y);
745 }
746 }
747 } else if (gui_grab) {
748 sdl_hide_cursor();
749 }
750 guest_cursor = on;
751 guest_x = x, guest_y = y;
752}
753
754static void sdl_mouse_define(DisplayChangeListener *dcl,
755 QEMUCursor *c)
756{
757
758 if (guest_sprite) {
759 SDL_FreeCursor(guest_sprite);
760 }
761
762 if (guest_sprite_surface) {
763 SDL_FreeSurface(guest_sprite_surface);
764 }
765
766 guest_sprite_surface =
767 SDL_CreateRGBSurfaceFrom(c->data, c->width, c->height, 32, c->width * 4,
768 0xff0000, 0x00ff00, 0xff, 0xff000000);
769
770 if (!guest_sprite_surface) {
771 fprintf(stderr, "Failed to make rgb surface from %p\n", c);
772 return;
773 }
774 guest_sprite = SDL_CreateColorCursor(guest_sprite_surface,
775 c->hot_x, c->hot_y);
776 if (!guest_sprite) {
777 fprintf(stderr, "Failed to make color cursor from %p\n", c);
778 return;
779 }
780 if (guest_cursor &&
781 (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
782 SDL_SetCursor(guest_sprite);
783 }
784}
785
786static void sdl_cleanup(void)
787{
788 if (guest_sprite) {
789 SDL_FreeCursor(guest_sprite);
790 }
791 SDL_QuitSubSystem(SDL_INIT_VIDEO);
792}
793
794static const DisplayChangeListenerOps dcl_ops = {
795 .dpy_name = "sdl",
796 .dpy_gfx_update = sdl_update,
797 .dpy_gfx_switch = sdl_switch,
798 .dpy_refresh = sdl_refresh,
799 .dpy_mouse_set = sdl_mouse_warp,
800 .dpy_cursor_define = sdl_mouse_define,
801};
802
803void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
804{
805 int flags;
806 uint8_t data = 0;
807 char *filename;
808 int i;
809
810 if (no_frame) {
811 gui_noframe = 1;
812 }
813
814#ifdef __linux__
815 /* on Linux, SDL may use fbcon|directfb|svgalib when run without
816 * accessible $DISPLAY to open X11 window. This is often the case
817 * when qemu is run using sudo. But in this case, and when actually
818 * run in X11 environment, SDL fights with X11 for the video card,
819 * making current display unavailable, often until reboot.
820 * So make x11 the default SDL video driver if this variable is unset.
821 * This is a bit hackish but saves us from bigger problem.
822 * Maybe it's a good idea to fix this in SDL instead.
823 */
824 setenv("SDL_VIDEODRIVER", "x11", 0);
825#endif
826
827 flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
828 if (SDL_Init(flags)) {
829 fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
830 SDL_GetError());
831 exit(1);
832 }
833
834 for (i = 0;; i++) {
835 QemuConsole *con = qemu_console_lookup_by_index(i);
836 if (!con || !qemu_console_is_graphic(con)) {
837 break;
838 }
839 }
840 sdl2_num_outputs = i;
841 sdl2_console = g_new0(struct sdl2_state, sdl2_num_outputs);
842 for (i = 0; i < sdl2_num_outputs; i++) {
843 QemuConsole *con = qemu_console_lookup_by_index(i);
844 sdl2_console[i].dcl.ops = &dcl_ops;
845 sdl2_console[i].dcl.con = con;
846 register_displaychangelistener(&sdl2_console[i].dcl);
847 sdl2_console[i].idx = i;
848 }
849
850 /* Load a 32x32x4 image. White pixels are transparent. */
851 filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp");
852 if (filename) {
853 SDL_Surface *image = SDL_LoadBMP(filename);
854 if (image) {
855 uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255);
856 SDL_SetColorKey(image, SDL_TRUE, colorkey);
857 SDL_SetWindowIcon(sdl2_console[0].real_window, image);
858 }
859 g_free(filename);
860 }
861
862 if (full_screen) {
863 gui_fullscreen = 1;
864 sdl_grab_start(0);
865 }
866
867 mouse_mode_notifier.notify = sdl_mouse_mode_change;
868 qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);
869
870 gui_grab = 0;
871
872 sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
873 sdl_cursor_normal = SDL_GetCursor();
874
875 atexit(sdl_cleanup);
876}
877#endif