blob: 47a757ad880cc208bc4c154a4d788c078a4d5208 [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>
Dave Airlie47c03742013-12-10 14:05:51 +100030#include <SDL_syswm.h>
31
32#include "qemu-common.h"
33#include "ui/console.h"
34#include "ui/input.h"
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +010035#include "ui/sdl2.h"
Dave Airlie47c03742013-12-10 14:05:51 +100036#include "sysemu/sysemu.h"
Dave Airlie47c03742013-12-10 14:05:51 +100037
Dave Airlie47c03742013-12-10 14:05:51 +100038static int sdl2_num_outputs;
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +010039static struct sdl2_console *sdl2_console;
Dave Airlie47c03742013-12-10 14:05:51 +100040
41static SDL_Surface *guest_sprite_surface;
42static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
43
44static bool gui_saved_scaling;
45static int gui_saved_width;
46static int gui_saved_height;
47static int gui_saved_grab;
48static int gui_fullscreen;
49static int gui_noframe;
50static int gui_key_modifier_pressed;
51static int gui_keysym;
52static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
Dave Airlie47c03742013-12-10 14:05:51 +100053static SDL_Cursor *sdl_cursor_normal;
54static SDL_Cursor *sdl_cursor_hidden;
55static int absolute_enabled;
56static int guest_cursor;
57static int guest_x, guest_y;
58static SDL_Cursor *guest_sprite;
59static int scaling_active;
60static Notifier mouse_mode_notifier;
61
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +010062static void sdl_update_caption(struct sdl2_console *scon);
Dave Airlie47c03742013-12-10 14:05:51 +100063
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +010064static struct sdl2_console *get_scon_from_window(uint32_t window_id)
Dave Airlie47c03742013-12-10 14:05:51 +100065{
66 int i;
67 for (i = 0; i < sdl2_num_outputs; i++) {
68 if (sdl2_console[i].real_window == SDL_GetWindowFromID(window_id)) {
69 return &sdl2_console[i];
70 }
71 }
72 return NULL;
73}
74
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +010075static void do_sdl_resize(struct sdl2_console *scon, int width, int height,
Dave Airlie47c03742013-12-10 14:05:51 +100076 int bpp)
77{
78 int flags;
79
80 if (scon->real_window && scon->real_renderer) {
81 if (width && height) {
82 SDL_RenderSetLogicalSize(scon->real_renderer, width, height);
83 SDL_SetWindowSize(scon->real_window, width, height);
84 } else {
85 SDL_DestroyRenderer(scon->real_renderer);
86 SDL_DestroyWindow(scon->real_window);
87 scon->real_renderer = NULL;
88 scon->real_window = NULL;
89 }
90 } else {
91 if (!width || !height) {
92 return;
93 }
94 flags = 0;
95 if (gui_fullscreen) {
96 flags |= SDL_WINDOW_FULLSCREEN;
97 } else {
98 flags |= SDL_WINDOW_RESIZABLE;
99 }
Gerd Hoffmann363f59d2014-05-27 09:44:39 +0200100 if (scon->hidden) {
101 flags |= SDL_WINDOW_HIDDEN;
102 }
Dave Airlie47c03742013-12-10 14:05:51 +1000103
104 scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED,
105 SDL_WINDOWPOS_UNDEFINED,
106 width, height, flags);
107 scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0);
108 sdl_update_caption(scon);
109 }
110}
111
112static void sdl_switch(DisplayChangeListener *dcl,
113 DisplaySurface *new_surface)
114{
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100115 struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
Dave Airlie47c03742013-12-10 14:05:51 +1000116 int format = 0;
117 int idx = scon->idx;
118 DisplaySurface *old_surface = scon->surface;
119
120 /* temporary hack: allows to call sdl_switch to handle scaling changes */
121 if (new_surface) {
122 scon->surface = new_surface;
123 }
124
125 if (!new_surface && idx > 0) {
126 scon->surface = NULL;
127 }
128
129 if (new_surface == NULL) {
130 do_sdl_resize(scon, 0, 0, 0);
131 } else {
132 do_sdl_resize(scon, surface_width(scon->surface),
133 surface_height(scon->surface), 0);
134 }
135
136 if (old_surface && scon->texture) {
137 SDL_DestroyTexture(scon->texture);
138 scon->texture = NULL;
139 }
140
141 if (new_surface) {
142 if (!scon->texture) {
143 if (surface_bits_per_pixel(scon->surface) == 16) {
144 format = SDL_PIXELFORMAT_RGB565;
145 } else if (surface_bits_per_pixel(scon->surface) == 32) {
146 format = SDL_PIXELFORMAT_ARGB8888;
147 }
148
149 scon->texture = SDL_CreateTexture(scon->real_renderer, format,
150 SDL_TEXTUREACCESS_STREAMING,
151 surface_width(new_surface),
152 surface_height(new_surface));
153 }
154 }
155}
156
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100157static void sdl_update_caption(struct sdl2_console *scon)
Dave Airlie47c03742013-12-10 14:05:51 +1000158{
159 char win_title[1024];
160 char icon_title[1024];
161 const char *status = "";
162
163 if (!runstate_is_running()) {
164 status = " [Stopped]";
165 } else if (gui_grab) {
166 if (alt_grab) {
Gerd Hoffmann44f017d2014-11-11 12:02:50 +0100167 status = " - Press Ctrl-Alt-Shift to exit grab";
Dave Airlie47c03742013-12-10 14:05:51 +1000168 } else if (ctrl_grab) {
Gerd Hoffmann44f017d2014-11-11 12:02:50 +0100169 status = " - Press Right-Ctrl to exit grab";
Dave Airlie47c03742013-12-10 14:05:51 +1000170 } else {
Gerd Hoffmann44f017d2014-11-11 12:02:50 +0100171 status = " - Press Ctrl-Alt to exit grab";
Dave Airlie47c03742013-12-10 14:05:51 +1000172 }
173 }
174
175 if (qemu_name) {
176 snprintf(win_title, sizeof(win_title), "QEMU (%s-%d)%s", qemu_name,
177 scon->idx, status);
178 snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name);
179 } else {
180 snprintf(win_title, sizeof(win_title), "QEMU%s", status);
181 snprintf(icon_title, sizeof(icon_title), "QEMU");
182 }
183
184 if (scon->real_window) {
185 SDL_SetWindowTitle(scon->real_window, win_title);
186 }
187}
188
189static void sdl_hide_cursor(void)
190{
191 if (!cursor_hide) {
192 return;
193 }
194
195 if (qemu_input_is_absolute()) {
196 SDL_ShowCursor(1);
197 SDL_SetCursor(sdl_cursor_hidden);
198 } else {
Cole Robinson2d968ff2014-04-01 16:37:11 -0400199 SDL_SetRelativeMouseMode(SDL_TRUE);
Dave Airlie47c03742013-12-10 14:05:51 +1000200 }
201}
202
203static void sdl_show_cursor(void)
204{
205 if (!cursor_hide) {
206 return;
207 }
208
209 if (!qemu_input_is_absolute()) {
Cole Robinson2d968ff2014-04-01 16:37:11 -0400210 SDL_SetRelativeMouseMode(SDL_FALSE);
Dave Airlie47c03742013-12-10 14:05:51 +1000211 SDL_ShowCursor(1);
212 if (guest_cursor &&
213 (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
214 SDL_SetCursor(guest_sprite);
215 } else {
216 SDL_SetCursor(sdl_cursor_normal);
217 }
218 }
219}
220
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100221static void sdl_grab_start(struct sdl2_console *scon)
Dave Airlie47c03742013-12-10 14:05:51 +1000222{
Gerd Hoffmannf2335792014-05-26 14:05:51 +0200223 QemuConsole *con = scon ? scon->dcl.con : NULL;
224
225 if (!con || !qemu_console_is_graphic(con)) {
226 return;
227 }
Dave Airlie47c03742013-12-10 14:05:51 +1000228 /*
229 * If the application is not active, do not try to enter grab state. This
230 * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the
231 * application (SDL bug).
232 */
233 if (!(SDL_GetWindowFlags(scon->real_window) & SDL_WINDOW_INPUT_FOCUS)) {
234 return;
235 }
236 if (guest_cursor) {
237 SDL_SetCursor(guest_sprite);
238 if (!qemu_input_is_absolute() && !absolute_enabled) {
239 SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y);
240 }
241 } else {
242 sdl_hide_cursor();
243 }
244 SDL_SetWindowGrab(scon->real_window, SDL_TRUE);
245 gui_grab = 1;
246 sdl_update_caption(scon);
247}
248
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100249static void sdl_grab_end(struct sdl2_console *scon)
Dave Airlie47c03742013-12-10 14:05:51 +1000250{
251 SDL_SetWindowGrab(scon->real_window, SDL_FALSE);
252 gui_grab = 0;
253 sdl_show_cursor();
254 sdl_update_caption(scon);
255}
256
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100257static void absolute_mouse_grab(struct sdl2_console *scon)
Dave Airlie47c03742013-12-10 14:05:51 +1000258{
259 int mouse_x, mouse_y;
260 int scr_w, scr_h;
261 SDL_GetMouseState(&mouse_x, &mouse_y);
262 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
263 if (mouse_x > 0 && mouse_x < scr_w - 1 &&
264 mouse_y > 0 && mouse_y < scr_h - 1) {
265 sdl_grab_start(scon);
266 }
267}
268
269static void sdl_mouse_mode_change(Notifier *notify, void *data)
270{
271 if (qemu_input_is_absolute()) {
272 if (!absolute_enabled) {
273 absolute_enabled = 1;
274 absolute_mouse_grab(&sdl2_console[0]);
275 }
276 } else if (absolute_enabled) {
277 if (!gui_fullscreen) {
278 sdl_grab_end(&sdl2_console[0]);
279 }
280 absolute_enabled = 0;
281 }
282}
283
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100284static void sdl_send_mouse_event(struct sdl2_console *scon, int dx, int dy,
Cole Robinson3f2fde22014-04-21 18:58:50 -0400285 int x, int y, int state)
Dave Airlie47c03742013-12-10 14:05:51 +1000286{
287 static uint32_t bmap[INPUT_BUTTON_MAX] = {
288 [INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT),
289 [INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE),
290 [INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT),
Dave Airlie47c03742013-12-10 14:05:51 +1000291 };
292 static uint32_t prev_state;
293
294 if (prev_state != state) {
295 qemu_input_update_buttons(scon->dcl.con, bmap, prev_state, state);
296 prev_state = state;
297 }
298
299 if (qemu_input_is_absolute()) {
300 int scr_w, scr_h;
301 int max_w = 0, max_h = 0;
302 int off_x = 0, off_y = 0;
303 int cur_off_x = 0, cur_off_y = 0;
304 int i;
305
306 for (i = 0; i < sdl2_num_outputs; i++) {
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100307 struct sdl2_console *thiscon = &sdl2_console[i];
Dave Airlie47c03742013-12-10 14:05:51 +1000308 if (thiscon->real_window && thiscon->surface) {
309 SDL_GetWindowSize(thiscon->real_window, &scr_w, &scr_h);
310 cur_off_x = thiscon->x;
311 cur_off_y = thiscon->y;
312 if (scr_w + cur_off_x > max_w) {
313 max_w = scr_w + cur_off_x;
314 }
315 if (scr_h + cur_off_y > max_h) {
316 max_h = scr_h + cur_off_y;
317 }
318 if (i == scon->idx) {
319 off_x = cur_off_x;
320 off_y = cur_off_y;
321 }
322 }
323 }
324 qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, max_w);
325 qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, off_y + y, max_h);
Cole Robinsonafbc0dd2014-04-01 16:37:10 -0400326 } else {
327 if (guest_cursor) {
328 x -= guest_x;
329 y -= guest_y;
330 guest_x += x;
331 guest_y += y;
332 dx = x;
333 dy = y;
334 }
335 qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_X, dx);
336 qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_Y, dy);
Dave Airlie47c03742013-12-10 14:05:51 +1000337 }
338 qemu_input_event_sync();
339}
340
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100341static void sdl_scale(struct sdl2_console *scon, int width, int height)
Dave Airlie47c03742013-12-10 14:05:51 +1000342{
343 int bpp = 0;
344 do_sdl_resize(scon, width, height, bpp);
345 scaling_active = 1;
346}
347
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100348static void toggle_full_screen(struct sdl2_console *scon)
Dave Airlie47c03742013-12-10 14:05:51 +1000349{
350 int width = surface_width(scon->surface);
351 int height = surface_height(scon->surface);
352 int bpp = surface_bits_per_pixel(scon->surface);
353
354 gui_fullscreen = !gui_fullscreen;
355 if (gui_fullscreen) {
356 SDL_GetWindowSize(scon->real_window,
357 &gui_saved_width, &gui_saved_height);
358 gui_saved_scaling = scaling_active;
359
360 do_sdl_resize(scon, width, height, bpp);
361 scaling_active = 0;
362
363 gui_saved_grab = gui_grab;
364 sdl_grab_start(scon);
365 } else {
366 if (gui_saved_scaling) {
367 sdl_scale(scon, gui_saved_width, gui_saved_height);
368 } else {
369 do_sdl_resize(scon, width, height, 0);
370 }
371 if (!gui_saved_grab) {
372 sdl_grab_end(scon);
373 }
374 }
375 graphic_hw_invalidate(scon->dcl.con);
376 graphic_hw_update(scon->dcl.con);
377}
378
379static void handle_keydown(SDL_Event *ev)
380{
Gerd Hoffmann363f59d2014-05-27 09:44:39 +0200381 int mod_state, win;
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100382 struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
Dave Airlie47c03742013-12-10 14:05:51 +1000383
384 if (alt_grab) {
385 mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) ==
386 (gui_grab_code | KMOD_LSHIFT);
387 } else if (ctrl_grab) {
388 mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL;
389 } else {
390 mod_state = (SDL_GetModState() & gui_grab_code) == gui_grab_code;
391 }
392 gui_key_modifier_pressed = mod_state;
393
394 if (gui_key_modifier_pressed) {
395 switch (ev->key.keysym.scancode) {
Gerd Hoffmann363f59d2014-05-27 09:44:39 +0200396 case SDL_SCANCODE_2:
397 case SDL_SCANCODE_3:
398 case SDL_SCANCODE_4:
399 case SDL_SCANCODE_5:
400 case SDL_SCANCODE_6:
401 case SDL_SCANCODE_7:
402 case SDL_SCANCODE_8:
403 case SDL_SCANCODE_9:
404 win = ev->key.keysym.scancode - SDL_SCANCODE_1;
405 if (win < sdl2_num_outputs) {
406 sdl2_console[win].hidden = !sdl2_console[win].hidden;
407 if (sdl2_console[win].real_window) {
408 if (sdl2_console[win].hidden) {
409 SDL_HideWindow(sdl2_console[win].real_window);
410 } else {
411 SDL_ShowWindow(sdl2_console[win].real_window);
412 }
413 }
414 gui_keysym = 1;
415 }
416 break;
Dave Airlie47c03742013-12-10 14:05:51 +1000417 case SDL_SCANCODE_F:
418 toggle_full_screen(scon);
419 gui_keysym = 1;
420 break;
421 case SDL_SCANCODE_U:
422 if (scaling_active) {
423 scaling_active = 0;
424 sdl_switch(&scon->dcl, NULL);
425 graphic_hw_invalidate(scon->dcl.con);
426 graphic_hw_update(scon->dcl.con);
427 }
428 gui_keysym = 1;
429 break;
430 case SDL_SCANCODE_KP_PLUS:
431 case SDL_SCANCODE_KP_MINUS:
432 if (!gui_fullscreen) {
433 int scr_w, scr_h;
434 int width, height;
435 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
436
437 width = MAX(scr_w + (ev->key.keysym.scancode ==
438 SDL_SCANCODE_KP_PLUS ? 50 : -50),
439 160);
440 height = (surface_height(scon->surface) * width) /
441 surface_width(scon->surface);
442
443 sdl_scale(scon, width, height);
444 graphic_hw_invalidate(NULL);
445 graphic_hw_update(NULL);
446 gui_keysym = 1;
447 }
448 default:
449 break;
450 }
451 }
452 if (!gui_keysym) {
Gerd Hoffmann8fc1a3f2014-11-11 10:58:19 +0100453 sdl2_process_key(scon, &ev->key);
Dave Airlie47c03742013-12-10 14:05:51 +1000454 }
455}
456
457static void handle_keyup(SDL_Event *ev)
458{
459 int mod_state;
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100460 struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
Dave Airlie47c03742013-12-10 14:05:51 +1000461
462 if (!alt_grab) {
463 mod_state = (ev->key.keysym.mod & gui_grab_code);
464 } else {
465 mod_state = (ev->key.keysym.mod & (gui_grab_code | KMOD_LSHIFT));
466 }
467 if (!mod_state && gui_key_modifier_pressed) {
468 gui_key_modifier_pressed = 0;
469 if (gui_keysym == 0) {
470 /* exit/enter grab if pressing Ctrl-Alt */
471 if (!gui_grab) {
472 sdl_grab_start(scon);
473 } else if (!gui_fullscreen) {
474 sdl_grab_end(scon);
475 }
476 /* SDL does not send back all the modifiers key, so we must
477 * correct it. */
Gerd Hoffmann8fc1a3f2014-11-11 10:58:19 +0100478 sdl2_reset_keys(scon);
Dave Airlie47c03742013-12-10 14:05:51 +1000479 return;
480 }
481 gui_keysym = 0;
482 }
483 if (!gui_keysym) {
Gerd Hoffmann8fc1a3f2014-11-11 10:58:19 +0100484 sdl2_process_key(scon, &ev->key);
Dave Airlie47c03742013-12-10 14:05:51 +1000485 }
486}
487
Gerd Hoffmannf2335792014-05-26 14:05:51 +0200488static void handle_textinput(SDL_Event *ev)
489{
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100490 struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
Gerd Hoffmannf2335792014-05-26 14:05:51 +0200491 QemuConsole *con = scon ? scon->dcl.con : NULL;
492
493 if (qemu_console_is_graphic(con)) {
494 return;
495 }
496 kbd_put_string_console(con, ev->text.text, strlen(ev->text.text));
497}
498
Dave Airlie47c03742013-12-10 14:05:51 +1000499static void handle_mousemotion(SDL_Event *ev)
500{
501 int max_x, max_y;
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100502 struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
Dave Airlie47c03742013-12-10 14:05:51 +1000503
504 if (qemu_input_is_absolute() || absolute_enabled) {
505 int scr_w, scr_h;
506 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
507 max_x = scr_w - 1;
508 max_y = scr_h - 1;
509 if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 ||
510 ev->motion.x == max_x || ev->motion.y == max_y)) {
511 sdl_grab_end(scon);
512 }
513 if (!gui_grab &&
514 (ev->motion.x > 0 && ev->motion.x < max_x &&
515 ev->motion.y > 0 && ev->motion.y < max_y)) {
516 sdl_grab_start(scon);
517 }
518 }
519 if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
Cole Robinson3f2fde22014-04-21 18:58:50 -0400520 sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel,
Dave Airlie47c03742013-12-10 14:05:51 +1000521 ev->motion.x, ev->motion.y, ev->motion.state);
522 }
523}
524
525static void handle_mousebutton(SDL_Event *ev)
526{
527 int buttonstate = SDL_GetMouseState(NULL, NULL);
528 SDL_MouseButtonEvent *bev;
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100529 struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
Dave Airlie47c03742013-12-10 14:05:51 +1000530
531 bev = &ev->button;
532 if (!gui_grab && !qemu_input_is_absolute()) {
533 if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
534 /* start grabbing all events */
535 sdl_grab_start(scon);
536 }
537 } else {
Dave Airlie47c03742013-12-10 14:05:51 +1000538 if (ev->type == SDL_MOUSEBUTTONDOWN) {
539 buttonstate |= SDL_BUTTON(bev->button);
540 } else {
541 buttonstate &= ~SDL_BUTTON(bev->button);
542 }
Cole Robinson3f2fde22014-04-21 18:58:50 -0400543 sdl_send_mouse_event(scon, 0, 0, bev->x, bev->y, buttonstate);
Dave Airlie47c03742013-12-10 14:05:51 +1000544 }
545}
546
Cole Robinson3f2fde22014-04-21 18:58:50 -0400547static void handle_mousewheel(SDL_Event *ev)
548{
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100549 struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
Cole Robinson3f2fde22014-04-21 18:58:50 -0400550 SDL_MouseWheelEvent *wev = &ev->wheel;
551 InputButton btn;
552
553 if (wev->y > 0) {
554 btn = INPUT_BUTTON_WHEEL_UP;
555 } else if (wev->y < 0) {
556 btn = INPUT_BUTTON_WHEEL_DOWN;
557 } else {
558 return;
559 }
560
561 qemu_input_queue_btn(scon->dcl.con, btn, true);
562 qemu_input_event_sync();
563 qemu_input_queue_btn(scon->dcl.con, btn, false);
564 qemu_input_event_sync();
565}
566
Dave Airlie47c03742013-12-10 14:05:51 +1000567static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev)
568{
569 int w, h;
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100570 struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
Dave Airlie47c03742013-12-10 14:05:51 +1000571
572 switch (ev->window.event) {
573 case SDL_WINDOWEVENT_RESIZED:
574 sdl_scale(scon, ev->window.data1, ev->window.data2);
Dave Airlie8b15d9f2014-03-25 16:50:36 +1000575 {
576 QemuUIInfo info;
577 memset(&info, 0, sizeof(info));
578 info.width = ev->window.data1;
579 info.height = ev->window.data2;
580 dpy_set_ui_info(scon->dcl.con, &info);
581 }
Dave Airlie47c03742013-12-10 14:05:51 +1000582 graphic_hw_invalidate(scon->dcl.con);
583 graphic_hw_update(scon->dcl.con);
584 break;
585 case SDL_WINDOWEVENT_EXPOSED:
586 SDL_GetWindowSize(SDL_GetWindowFromID(ev->window.windowID), &w, &h);
Gerd Hoffmannf1ddebd2014-11-11 11:09:26 +0100587 sdl2_2d_update(dcl, 0, 0, w, h);
Dave Airlie47c03742013-12-10 14:05:51 +1000588 break;
589 case SDL_WINDOWEVENT_FOCUS_GAINED:
590 case SDL_WINDOWEVENT_ENTER:
591 if (!gui_grab && (qemu_input_is_absolute() || absolute_enabled)) {
592 absolute_mouse_grab(scon);
593 }
594 break;
595 case SDL_WINDOWEVENT_FOCUS_LOST:
596 if (gui_grab && !gui_fullscreen) {
597 sdl_grab_end(scon);
598 }
599 break;
600 case SDL_WINDOWEVENT_RESTORED:
601 update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT);
602 break;
603 case SDL_WINDOWEVENT_MINIMIZED:
604 update_displaychangelistener(dcl, 500);
605 break;
606 case SDL_WINDOWEVENT_CLOSE:
607 if (!no_quit) {
608 no_shutdown = 0;
609 qemu_system_shutdown_request();
610 }
611 break;
612 }
613}
614
615static void sdl_refresh(DisplayChangeListener *dcl)
616{
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100617 struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
Dave Airlie47c03742013-12-10 14:05:51 +1000618 SDL_Event ev1, *ev = &ev1;
619
620 if (scon->last_vm_running != runstate_is_running()) {
621 scon->last_vm_running = runstate_is_running();
622 sdl_update_caption(scon);
623 }
624
625 graphic_hw_update(dcl->con);
626
627 while (SDL_PollEvent(ev)) {
628 switch (ev->type) {
629 case SDL_KEYDOWN:
630 handle_keydown(ev);
631 break;
632 case SDL_KEYUP:
633 handle_keyup(ev);
634 break;
Gerd Hoffmannf2335792014-05-26 14:05:51 +0200635 case SDL_TEXTINPUT:
636 handle_textinput(ev);
637 break;
Dave Airlie47c03742013-12-10 14:05:51 +1000638 case SDL_QUIT:
639 if (!no_quit) {
640 no_shutdown = 0;
641 qemu_system_shutdown_request();
642 }
643 break;
644 case SDL_MOUSEMOTION:
645 handle_mousemotion(ev);
646 break;
647 case SDL_MOUSEBUTTONDOWN:
648 case SDL_MOUSEBUTTONUP:
649 handle_mousebutton(ev);
650 break;
Cole Robinson3f2fde22014-04-21 18:58:50 -0400651 case SDL_MOUSEWHEEL:
652 handle_mousewheel(ev);
653 break;
Dave Airlie47c03742013-12-10 14:05:51 +1000654 case SDL_WINDOWEVENT:
655 handle_windowevent(dcl, ev);
656 break;
657 default:
658 break;
659 }
660 }
661}
662
663static void sdl_mouse_warp(DisplayChangeListener *dcl,
664 int x, int y, int on)
665{
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100666 struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
Dave Airlie47c03742013-12-10 14:05:51 +1000667 if (on) {
668 if (!guest_cursor) {
669 sdl_show_cursor();
670 }
671 if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
672 SDL_SetCursor(guest_sprite);
673 if (!qemu_input_is_absolute() && !absolute_enabled) {
674 SDL_WarpMouseInWindow(scon->real_window, x, y);
675 }
676 }
677 } else if (gui_grab) {
678 sdl_hide_cursor();
679 }
680 guest_cursor = on;
681 guest_x = x, guest_y = y;
682}
683
684static void sdl_mouse_define(DisplayChangeListener *dcl,
685 QEMUCursor *c)
686{
687
688 if (guest_sprite) {
689 SDL_FreeCursor(guest_sprite);
690 }
691
692 if (guest_sprite_surface) {
693 SDL_FreeSurface(guest_sprite_surface);
694 }
695
696 guest_sprite_surface =
697 SDL_CreateRGBSurfaceFrom(c->data, c->width, c->height, 32, c->width * 4,
698 0xff0000, 0x00ff00, 0xff, 0xff000000);
699
700 if (!guest_sprite_surface) {
701 fprintf(stderr, "Failed to make rgb surface from %p\n", c);
702 return;
703 }
704 guest_sprite = SDL_CreateColorCursor(guest_sprite_surface,
705 c->hot_x, c->hot_y);
706 if (!guest_sprite) {
707 fprintf(stderr, "Failed to make color cursor from %p\n", c);
708 return;
709 }
710 if (guest_cursor &&
711 (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
712 SDL_SetCursor(guest_sprite);
713 }
714}
715
716static void sdl_cleanup(void)
717{
718 if (guest_sprite) {
719 SDL_FreeCursor(guest_sprite);
720 }
721 SDL_QuitSubSystem(SDL_INIT_VIDEO);
722}
723
Gerd Hoffmannf1ddebd2014-11-11 11:09:26 +0100724static const DisplayChangeListenerOps dcl_2d_ops = {
725 .dpy_name = "sdl2-2d",
726 .dpy_gfx_update = sdl2_2d_update,
Dave Airlie47c03742013-12-10 14:05:51 +1000727 .dpy_gfx_switch = sdl_switch,
728 .dpy_refresh = sdl_refresh,
729 .dpy_mouse_set = sdl_mouse_warp,
730 .dpy_cursor_define = sdl_mouse_define,
731};
732
733void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
734{
735 int flags;
736 uint8_t data = 0;
737 char *filename;
738 int i;
739
740 if (no_frame) {
741 gui_noframe = 1;
742 }
743
744#ifdef __linux__
745 /* on Linux, SDL may use fbcon|directfb|svgalib when run without
746 * accessible $DISPLAY to open X11 window. This is often the case
747 * when qemu is run using sudo. But in this case, and when actually
748 * run in X11 environment, SDL fights with X11 for the video card,
749 * making current display unavailable, often until reboot.
750 * So make x11 the default SDL video driver if this variable is unset.
751 * This is a bit hackish but saves us from bigger problem.
752 * Maybe it's a good idea to fix this in SDL instead.
753 */
754 setenv("SDL_VIDEODRIVER", "x11", 0);
755#endif
756
757 flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
758 if (SDL_Init(flags)) {
759 fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
760 SDL_GetError());
761 exit(1);
762 }
Gerd Hoffmann44f017d2014-11-11 12:02:50 +0100763 SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "1");
Dave Airlie47c03742013-12-10 14:05:51 +1000764
765 for (i = 0;; i++) {
766 QemuConsole *con = qemu_console_lookup_by_index(i);
Gerd Hoffmannf2335792014-05-26 14:05:51 +0200767 if (!con) {
Dave Airlie47c03742013-12-10 14:05:51 +1000768 break;
769 }
770 }
771 sdl2_num_outputs = i;
Gerd Hoffmann5d0fe652014-11-11 10:21:24 +0100772 sdl2_console = g_new0(struct sdl2_console, sdl2_num_outputs);
Dave Airlie47c03742013-12-10 14:05:51 +1000773 for (i = 0; i < sdl2_num_outputs; i++) {
774 QemuConsole *con = qemu_console_lookup_by_index(i);
Gerd Hoffmannf2335792014-05-26 14:05:51 +0200775 if (!qemu_console_is_graphic(con)) {
776 sdl2_console[i].hidden = true;
777 }
Gerd Hoffmannf1ddebd2014-11-11 11:09:26 +0100778 sdl2_console[i].dcl.ops = &dcl_2d_ops;
Dave Airlie47c03742013-12-10 14:05:51 +1000779 sdl2_console[i].dcl.con = con;
780 register_displaychangelistener(&sdl2_console[i].dcl);
781 sdl2_console[i].idx = i;
782 }
783
784 /* Load a 32x32x4 image. White pixels are transparent. */
785 filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp");
786 if (filename) {
787 SDL_Surface *image = SDL_LoadBMP(filename);
788 if (image) {
789 uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255);
790 SDL_SetColorKey(image, SDL_TRUE, colorkey);
791 SDL_SetWindowIcon(sdl2_console[0].real_window, image);
792 }
793 g_free(filename);
794 }
795
796 if (full_screen) {
797 gui_fullscreen = 1;
798 sdl_grab_start(0);
799 }
800
801 mouse_mode_notifier.notify = sdl_mouse_mode_change;
802 qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);
803
804 gui_grab = 0;
805
806 sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
807 sdl_cursor_normal = SDL_GetCursor();
808
809 atexit(sdl_cleanup);
810}