os-posix: use global daemon_pipe instead of cryptic fds[1]
[qemu/ar7.git] / ui / sdl2.c
blob1ad74baafcd567649edf4f8e93ae90d888fafc4a
1 /*
2 * QEMU SDL display driver
4 * Copyright (c) 2003 Fabrice Bellard
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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.
24 /* Ported SDL 1.2 code to 2.0 by Dave Airlie. */
26 /* Avoid compiler warning because macro is redefined in SDL_syswm.h. */
27 #undef WIN32_LEAN_AND_MEAN
29 #include <SDL.h>
31 #if SDL_MAJOR_VERSION == 2
32 #include <SDL_syswm.h>
34 #include "qemu-common.h"
35 #include "ui/console.h"
36 #include "ui/input.h"
37 #include "sysemu/sysemu.h"
39 #include "sdl2-keymap.h"
41 static int sdl2_num_outputs;
42 static struct sdl2_state {
43 DisplayChangeListener dcl;
44 DisplaySurface *surface;
45 SDL_Texture *texture;
46 SDL_Window *real_window;
47 SDL_Renderer *real_renderer;
48 int idx;
49 int last_vm_running; /* per console for caption reasons */
50 int x, y;
51 int hidden;
52 } *sdl2_console;
54 static SDL_Surface *guest_sprite_surface;
55 static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
57 static bool gui_saved_scaling;
58 static int gui_saved_width;
59 static int gui_saved_height;
60 static int gui_saved_grab;
61 static int gui_fullscreen;
62 static int gui_noframe;
63 static int gui_key_modifier_pressed;
64 static int gui_keysym;
65 static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
66 static uint8_t modifiers_state[SDL_NUM_SCANCODES];
67 static SDL_Cursor *sdl_cursor_normal;
68 static SDL_Cursor *sdl_cursor_hidden;
69 static int absolute_enabled;
70 static int guest_cursor;
71 static int guest_x, guest_y;
72 static SDL_Cursor *guest_sprite;
73 static int scaling_active;
74 static Notifier mouse_mode_notifier;
76 static void sdl_update_caption(struct sdl2_state *scon);
78 static struct sdl2_state *get_scon_from_window(uint32_t window_id)
80 int i;
81 for (i = 0; i < sdl2_num_outputs; i++) {
82 if (sdl2_console[i].real_window == SDL_GetWindowFromID(window_id)) {
83 return &sdl2_console[i];
86 return NULL;
89 static void sdl_update(DisplayChangeListener *dcl,
90 int x, int y, int w, int h)
92 struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
93 SDL_Rect rect;
94 DisplaySurface *surf = qemu_console_surface(dcl->con);
96 if (!surf) {
97 return;
99 if (!scon->texture) {
100 return;
103 rect.x = x;
104 rect.y = y;
105 rect.w = w;
106 rect.h = h;
108 SDL_UpdateTexture(scon->texture, NULL, surface_data(surf),
109 surface_stride(surf));
110 SDL_RenderCopy(scon->real_renderer, scon->texture, &rect, &rect);
111 SDL_RenderPresent(scon->real_renderer);
114 static void do_sdl_resize(struct sdl2_state *scon, int width, int height,
115 int bpp)
117 int flags;
119 if (scon->real_window && scon->real_renderer) {
120 if (width && height) {
121 SDL_RenderSetLogicalSize(scon->real_renderer, width, height);
122 SDL_SetWindowSize(scon->real_window, width, height);
123 } else {
124 SDL_DestroyRenderer(scon->real_renderer);
125 SDL_DestroyWindow(scon->real_window);
126 scon->real_renderer = NULL;
127 scon->real_window = NULL;
129 } else {
130 if (!width || !height) {
131 return;
133 flags = 0;
134 if (gui_fullscreen) {
135 flags |= SDL_WINDOW_FULLSCREEN;
136 } else {
137 flags |= SDL_WINDOW_RESIZABLE;
139 if (scon->hidden) {
140 flags |= SDL_WINDOW_HIDDEN;
143 scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED,
144 SDL_WINDOWPOS_UNDEFINED,
145 width, height, flags);
146 scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0);
147 sdl_update_caption(scon);
151 static void sdl_switch(DisplayChangeListener *dcl,
152 DisplaySurface *new_surface)
154 struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
155 int format = 0;
156 int idx = scon->idx;
157 DisplaySurface *old_surface = scon->surface;
159 /* temporary hack: allows to call sdl_switch to handle scaling changes */
160 if (new_surface) {
161 scon->surface = new_surface;
164 if (!new_surface && idx > 0) {
165 scon->surface = NULL;
168 if (new_surface == NULL) {
169 do_sdl_resize(scon, 0, 0, 0);
170 } else {
171 do_sdl_resize(scon, surface_width(scon->surface),
172 surface_height(scon->surface), 0);
175 if (old_surface && scon->texture) {
176 SDL_DestroyTexture(scon->texture);
177 scon->texture = NULL;
180 if (new_surface) {
181 if (!scon->texture) {
182 if (surface_bits_per_pixel(scon->surface) == 16) {
183 format = SDL_PIXELFORMAT_RGB565;
184 } else if (surface_bits_per_pixel(scon->surface) == 32) {
185 format = SDL_PIXELFORMAT_ARGB8888;
188 scon->texture = SDL_CreateTexture(scon->real_renderer, format,
189 SDL_TEXTUREACCESS_STREAMING,
190 surface_width(new_surface),
191 surface_height(new_surface));
196 static void reset_keys(struct sdl2_state *scon)
198 QemuConsole *con = scon ? scon->dcl.con : NULL;
199 int i;
201 for (i = 0; i < 256; i++) {
202 if (modifiers_state[i]) {
203 int qcode = sdl2_scancode_to_qcode[i];
204 qemu_input_event_send_key_qcode(con, qcode, false);
205 modifiers_state[i] = 0;
210 static void sdl_process_key(struct sdl2_state *scon,
211 SDL_KeyboardEvent *ev)
213 int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode];
214 QemuConsole *con = scon ? scon->dcl.con : NULL;
216 if (!qemu_console_is_graphic(con)) {
217 if (ev->type == SDL_KEYDOWN) {
218 switch (ev->keysym.scancode) {
219 case SDL_SCANCODE_RETURN:
220 kbd_put_keysym_console(con, '\n');
221 break;
222 case SDL_SCANCODE_BACKSPACE:
223 kbd_put_keysym_console(con, QEMU_KEY_BACKSPACE);
224 break;
225 default:
226 kbd_put_qcode_console(con, qcode);
227 break;
230 return;
233 switch (ev->keysym.scancode) {
234 #if 0
235 case SDL_SCANCODE_NUMLOCKCLEAR:
236 case SDL_SCANCODE_CAPSLOCK:
237 /* SDL does not send the key up event, so we generate it */
238 qemu_input_event_send_key_qcode(con, qcode, true);
239 qemu_input_event_send_key_qcode(con, qcode, false);
240 return;
241 #endif
242 case SDL_SCANCODE_LCTRL:
243 case SDL_SCANCODE_LSHIFT:
244 case SDL_SCANCODE_LALT:
245 case SDL_SCANCODE_LGUI:
246 case SDL_SCANCODE_RCTRL:
247 case SDL_SCANCODE_RSHIFT:
248 case SDL_SCANCODE_RALT:
249 case SDL_SCANCODE_RGUI:
250 if (ev->type == SDL_KEYUP) {
251 modifiers_state[ev->keysym.scancode] = 0;
252 } else {
253 modifiers_state[ev->keysym.scancode] = 1;
255 /* fall though */
256 default:
257 qemu_input_event_send_key_qcode(con, qcode,
258 ev->type == SDL_KEYDOWN);
262 static void sdl_update_caption(struct sdl2_state *scon)
264 char win_title[1024];
265 char icon_title[1024];
266 const char *status = "";
268 if (!runstate_is_running()) {
269 status = " [Stopped]";
270 } else if (gui_grab) {
271 if (alt_grab) {
272 status = " - Press Ctrl-Alt-Shift to exit mouse grab";
273 } else if (ctrl_grab) {
274 status = " - Press Right-Ctrl to exit mouse grab";
275 } else {
276 status = " - Press Ctrl-Alt to exit mouse grab";
280 if (qemu_name) {
281 snprintf(win_title, sizeof(win_title), "QEMU (%s-%d)%s", qemu_name,
282 scon->idx, status);
283 snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name);
284 } else {
285 snprintf(win_title, sizeof(win_title), "QEMU%s", status);
286 snprintf(icon_title, sizeof(icon_title), "QEMU");
289 if (scon->real_window) {
290 SDL_SetWindowTitle(scon->real_window, win_title);
294 static void sdl_hide_cursor(void)
296 if (!cursor_hide) {
297 return;
300 if (qemu_input_is_absolute()) {
301 SDL_ShowCursor(1);
302 SDL_SetCursor(sdl_cursor_hidden);
303 } else {
304 SDL_SetRelativeMouseMode(SDL_TRUE);
308 static void sdl_show_cursor(void)
310 if (!cursor_hide) {
311 return;
314 if (!qemu_input_is_absolute()) {
315 SDL_SetRelativeMouseMode(SDL_FALSE);
316 SDL_ShowCursor(1);
317 if (guest_cursor &&
318 (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
319 SDL_SetCursor(guest_sprite);
320 } else {
321 SDL_SetCursor(sdl_cursor_normal);
326 static void sdl_grab_start(struct sdl2_state *scon)
328 QemuConsole *con = scon ? scon->dcl.con : NULL;
330 if (!con || !qemu_console_is_graphic(con)) {
331 return;
334 * If the application is not active, do not try to enter grab state. This
335 * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the
336 * application (SDL bug).
338 if (!(SDL_GetWindowFlags(scon->real_window) & SDL_WINDOW_INPUT_FOCUS)) {
339 return;
341 if (guest_cursor) {
342 SDL_SetCursor(guest_sprite);
343 if (!qemu_input_is_absolute() && !absolute_enabled) {
344 SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y);
346 } else {
347 sdl_hide_cursor();
349 SDL_SetWindowGrab(scon->real_window, SDL_TRUE);
350 gui_grab = 1;
351 sdl_update_caption(scon);
354 static void sdl_grab_end(struct sdl2_state *scon)
356 SDL_SetWindowGrab(scon->real_window, SDL_FALSE);
357 gui_grab = 0;
358 sdl_show_cursor();
359 sdl_update_caption(scon);
362 static void absolute_mouse_grab(struct sdl2_state *scon)
364 int mouse_x, mouse_y;
365 int scr_w, scr_h;
366 SDL_GetMouseState(&mouse_x, &mouse_y);
367 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
368 if (mouse_x > 0 && mouse_x < scr_w - 1 &&
369 mouse_y > 0 && mouse_y < scr_h - 1) {
370 sdl_grab_start(scon);
374 static void sdl_mouse_mode_change(Notifier *notify, void *data)
376 if (qemu_input_is_absolute()) {
377 if (!absolute_enabled) {
378 absolute_enabled = 1;
379 absolute_mouse_grab(&sdl2_console[0]);
381 } else if (absolute_enabled) {
382 if (!gui_fullscreen) {
383 sdl_grab_end(&sdl2_console[0]);
385 absolute_enabled = 0;
389 static void sdl_send_mouse_event(struct sdl2_state *scon, int dx, int dy,
390 int x, int y, int state)
392 static uint32_t bmap[INPUT_BUTTON_MAX] = {
393 [INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT),
394 [INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE),
395 [INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT),
397 static uint32_t prev_state;
399 if (prev_state != state) {
400 qemu_input_update_buttons(scon->dcl.con, bmap, prev_state, state);
401 prev_state = state;
404 if (qemu_input_is_absolute()) {
405 int scr_w, scr_h;
406 int max_w = 0, max_h = 0;
407 int off_x = 0, off_y = 0;
408 int cur_off_x = 0, cur_off_y = 0;
409 int i;
411 for (i = 0; i < sdl2_num_outputs; i++) {
412 struct sdl2_state *thiscon = &sdl2_console[i];
413 if (thiscon->real_window && thiscon->surface) {
414 SDL_GetWindowSize(thiscon->real_window, &scr_w, &scr_h);
415 cur_off_x = thiscon->x;
416 cur_off_y = thiscon->y;
417 if (scr_w + cur_off_x > max_w) {
418 max_w = scr_w + cur_off_x;
420 if (scr_h + cur_off_y > max_h) {
421 max_h = scr_h + cur_off_y;
423 if (i == scon->idx) {
424 off_x = cur_off_x;
425 off_y = cur_off_y;
429 qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, max_w);
430 qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, off_y + y, max_h);
431 } else {
432 if (guest_cursor) {
433 x -= guest_x;
434 y -= guest_y;
435 guest_x += x;
436 guest_y += y;
437 dx = x;
438 dy = y;
440 qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_X, dx);
441 qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_Y, dy);
443 qemu_input_event_sync();
446 static void sdl_scale(struct sdl2_state *scon, int width, int height)
448 int bpp = 0;
449 do_sdl_resize(scon, width, height, bpp);
450 scaling_active = 1;
453 static void toggle_full_screen(struct sdl2_state *scon)
455 int width = surface_width(scon->surface);
456 int height = surface_height(scon->surface);
457 int bpp = surface_bits_per_pixel(scon->surface);
459 gui_fullscreen = !gui_fullscreen;
460 if (gui_fullscreen) {
461 SDL_GetWindowSize(scon->real_window,
462 &gui_saved_width, &gui_saved_height);
463 gui_saved_scaling = scaling_active;
465 do_sdl_resize(scon, width, height, bpp);
466 scaling_active = 0;
468 gui_saved_grab = gui_grab;
469 sdl_grab_start(scon);
470 } else {
471 if (gui_saved_scaling) {
472 sdl_scale(scon, gui_saved_width, gui_saved_height);
473 } else {
474 do_sdl_resize(scon, width, height, 0);
476 if (!gui_saved_grab) {
477 sdl_grab_end(scon);
480 graphic_hw_invalidate(scon->dcl.con);
481 graphic_hw_update(scon->dcl.con);
484 static void handle_keydown(SDL_Event *ev)
486 int mod_state, win;
487 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
489 if (alt_grab) {
490 mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) ==
491 (gui_grab_code | KMOD_LSHIFT);
492 } else if (ctrl_grab) {
493 mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL;
494 } else {
495 mod_state = (SDL_GetModState() & gui_grab_code) == gui_grab_code;
497 gui_key_modifier_pressed = mod_state;
499 if (gui_key_modifier_pressed) {
500 switch (ev->key.keysym.scancode) {
501 case SDL_SCANCODE_2:
502 case SDL_SCANCODE_3:
503 case SDL_SCANCODE_4:
504 case SDL_SCANCODE_5:
505 case SDL_SCANCODE_6:
506 case SDL_SCANCODE_7:
507 case SDL_SCANCODE_8:
508 case SDL_SCANCODE_9:
509 win = ev->key.keysym.scancode - SDL_SCANCODE_1;
510 if (win < sdl2_num_outputs) {
511 sdl2_console[win].hidden = !sdl2_console[win].hidden;
512 if (sdl2_console[win].real_window) {
513 if (sdl2_console[win].hidden) {
514 SDL_HideWindow(sdl2_console[win].real_window);
515 } else {
516 SDL_ShowWindow(sdl2_console[win].real_window);
519 gui_keysym = 1;
521 break;
522 case SDL_SCANCODE_F:
523 toggle_full_screen(scon);
524 gui_keysym = 1;
525 break;
526 case SDL_SCANCODE_U:
527 if (scaling_active) {
528 scaling_active = 0;
529 sdl_switch(&scon->dcl, NULL);
530 graphic_hw_invalidate(scon->dcl.con);
531 graphic_hw_update(scon->dcl.con);
533 gui_keysym = 1;
534 break;
535 case SDL_SCANCODE_KP_PLUS:
536 case SDL_SCANCODE_KP_MINUS:
537 if (!gui_fullscreen) {
538 int scr_w, scr_h;
539 int width, height;
540 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
542 width = MAX(scr_w + (ev->key.keysym.scancode ==
543 SDL_SCANCODE_KP_PLUS ? 50 : -50),
544 160);
545 height = (surface_height(scon->surface) * width) /
546 surface_width(scon->surface);
548 sdl_scale(scon, width, height);
549 graphic_hw_invalidate(NULL);
550 graphic_hw_update(NULL);
551 gui_keysym = 1;
553 default:
554 break;
557 if (!gui_keysym) {
558 sdl_process_key(scon, &ev->key);
562 static void handle_keyup(SDL_Event *ev)
564 int mod_state;
565 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
567 if (!alt_grab) {
568 mod_state = (ev->key.keysym.mod & gui_grab_code);
569 } else {
570 mod_state = (ev->key.keysym.mod & (gui_grab_code | KMOD_LSHIFT));
572 if (!mod_state && gui_key_modifier_pressed) {
573 gui_key_modifier_pressed = 0;
574 if (gui_keysym == 0) {
575 /* exit/enter grab if pressing Ctrl-Alt */
576 if (!gui_grab) {
577 sdl_grab_start(scon);
578 } else if (!gui_fullscreen) {
579 sdl_grab_end(scon);
581 /* SDL does not send back all the modifiers key, so we must
582 * correct it. */
583 reset_keys(scon);
584 return;
586 gui_keysym = 0;
588 if (!gui_keysym) {
589 sdl_process_key(scon, &ev->key);
593 static void handle_textinput(SDL_Event *ev)
595 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
596 QemuConsole *con = scon ? scon->dcl.con : NULL;
598 if (qemu_console_is_graphic(con)) {
599 return;
601 kbd_put_string_console(con, ev->text.text, strlen(ev->text.text));
604 static void handle_mousemotion(SDL_Event *ev)
606 int max_x, max_y;
607 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
609 if (qemu_input_is_absolute() || absolute_enabled) {
610 int scr_w, scr_h;
611 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
612 max_x = scr_w - 1;
613 max_y = scr_h - 1;
614 if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 ||
615 ev->motion.x == max_x || ev->motion.y == max_y)) {
616 sdl_grab_end(scon);
618 if (!gui_grab &&
619 (ev->motion.x > 0 && ev->motion.x < max_x &&
620 ev->motion.y > 0 && ev->motion.y < max_y)) {
621 sdl_grab_start(scon);
624 if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
625 sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel,
626 ev->motion.x, ev->motion.y, ev->motion.state);
630 static void handle_mousebutton(SDL_Event *ev)
632 int buttonstate = SDL_GetMouseState(NULL, NULL);
633 SDL_MouseButtonEvent *bev;
634 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
636 bev = &ev->button;
637 if (!gui_grab && !qemu_input_is_absolute()) {
638 if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
639 /* start grabbing all events */
640 sdl_grab_start(scon);
642 } else {
643 if (ev->type == SDL_MOUSEBUTTONDOWN) {
644 buttonstate |= SDL_BUTTON(bev->button);
645 } else {
646 buttonstate &= ~SDL_BUTTON(bev->button);
648 sdl_send_mouse_event(scon, 0, 0, bev->x, bev->y, buttonstate);
652 static void handle_mousewheel(SDL_Event *ev)
654 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
655 SDL_MouseWheelEvent *wev = &ev->wheel;
656 InputButton btn;
658 if (wev->y > 0) {
659 btn = INPUT_BUTTON_WHEEL_UP;
660 } else if (wev->y < 0) {
661 btn = INPUT_BUTTON_WHEEL_DOWN;
662 } else {
663 return;
666 qemu_input_queue_btn(scon->dcl.con, btn, true);
667 qemu_input_event_sync();
668 qemu_input_queue_btn(scon->dcl.con, btn, false);
669 qemu_input_event_sync();
672 static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev)
674 int w, h;
675 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
677 switch (ev->window.event) {
678 case SDL_WINDOWEVENT_RESIZED:
679 sdl_scale(scon, ev->window.data1, ev->window.data2);
681 QemuUIInfo info;
682 memset(&info, 0, sizeof(info));
683 info.width = ev->window.data1;
684 info.height = ev->window.data2;
685 dpy_set_ui_info(scon->dcl.con, &info);
687 graphic_hw_invalidate(scon->dcl.con);
688 graphic_hw_update(scon->dcl.con);
689 break;
690 case SDL_WINDOWEVENT_EXPOSED:
691 SDL_GetWindowSize(SDL_GetWindowFromID(ev->window.windowID), &w, &h);
692 sdl_update(dcl, 0, 0, w, h);
693 break;
694 case SDL_WINDOWEVENT_FOCUS_GAINED:
695 case SDL_WINDOWEVENT_ENTER:
696 if (!gui_grab && (qemu_input_is_absolute() || absolute_enabled)) {
697 absolute_mouse_grab(scon);
699 break;
700 case SDL_WINDOWEVENT_FOCUS_LOST:
701 if (gui_grab && !gui_fullscreen) {
702 sdl_grab_end(scon);
704 break;
705 case SDL_WINDOWEVENT_RESTORED:
706 update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT);
707 break;
708 case SDL_WINDOWEVENT_MINIMIZED:
709 update_displaychangelistener(dcl, 500);
710 break;
711 case SDL_WINDOWEVENT_CLOSE:
712 if (!no_quit) {
713 no_shutdown = 0;
714 qemu_system_shutdown_request();
716 break;
720 static void sdl_refresh(DisplayChangeListener *dcl)
722 struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
723 SDL_Event ev1, *ev = &ev1;
725 if (scon->last_vm_running != runstate_is_running()) {
726 scon->last_vm_running = runstate_is_running();
727 sdl_update_caption(scon);
730 graphic_hw_update(dcl->con);
732 while (SDL_PollEvent(ev)) {
733 switch (ev->type) {
734 case SDL_KEYDOWN:
735 handle_keydown(ev);
736 break;
737 case SDL_KEYUP:
738 handle_keyup(ev);
739 break;
740 case SDL_TEXTINPUT:
741 handle_textinput(ev);
742 break;
743 case SDL_QUIT:
744 if (!no_quit) {
745 no_shutdown = 0;
746 qemu_system_shutdown_request();
748 break;
749 case SDL_MOUSEMOTION:
750 handle_mousemotion(ev);
751 break;
752 case SDL_MOUSEBUTTONDOWN:
753 case SDL_MOUSEBUTTONUP:
754 handle_mousebutton(ev);
755 break;
756 case SDL_MOUSEWHEEL:
757 handle_mousewheel(ev);
758 break;
759 case SDL_WINDOWEVENT:
760 handle_windowevent(dcl, ev);
761 break;
762 default:
763 break;
768 static void sdl_mouse_warp(DisplayChangeListener *dcl,
769 int x, int y, int on)
771 struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
772 if (on) {
773 if (!guest_cursor) {
774 sdl_show_cursor();
776 if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
777 SDL_SetCursor(guest_sprite);
778 if (!qemu_input_is_absolute() && !absolute_enabled) {
779 SDL_WarpMouseInWindow(scon->real_window, x, y);
782 } else if (gui_grab) {
783 sdl_hide_cursor();
785 guest_cursor = on;
786 guest_x = x, guest_y = y;
789 static void sdl_mouse_define(DisplayChangeListener *dcl,
790 QEMUCursor *c)
793 if (guest_sprite) {
794 SDL_FreeCursor(guest_sprite);
797 if (guest_sprite_surface) {
798 SDL_FreeSurface(guest_sprite_surface);
801 guest_sprite_surface =
802 SDL_CreateRGBSurfaceFrom(c->data, c->width, c->height, 32, c->width * 4,
803 0xff0000, 0x00ff00, 0xff, 0xff000000);
805 if (!guest_sprite_surface) {
806 fprintf(stderr, "Failed to make rgb surface from %p\n", c);
807 return;
809 guest_sprite = SDL_CreateColorCursor(guest_sprite_surface,
810 c->hot_x, c->hot_y);
811 if (!guest_sprite) {
812 fprintf(stderr, "Failed to make color cursor from %p\n", c);
813 return;
815 if (guest_cursor &&
816 (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
817 SDL_SetCursor(guest_sprite);
821 static void sdl_cleanup(void)
823 if (guest_sprite) {
824 SDL_FreeCursor(guest_sprite);
826 SDL_QuitSubSystem(SDL_INIT_VIDEO);
829 static const DisplayChangeListenerOps dcl_ops = {
830 .dpy_name = "sdl",
831 .dpy_gfx_update = sdl_update,
832 .dpy_gfx_switch = sdl_switch,
833 .dpy_refresh = sdl_refresh,
834 .dpy_mouse_set = sdl_mouse_warp,
835 .dpy_cursor_define = sdl_mouse_define,
838 void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
840 int flags;
841 uint8_t data = 0;
842 char *filename;
843 int i;
845 if (no_frame) {
846 gui_noframe = 1;
849 #ifdef __linux__
850 /* on Linux, SDL may use fbcon|directfb|svgalib when run without
851 * accessible $DISPLAY to open X11 window. This is often the case
852 * when qemu is run using sudo. But in this case, and when actually
853 * run in X11 environment, SDL fights with X11 for the video card,
854 * making current display unavailable, often until reboot.
855 * So make x11 the default SDL video driver if this variable is unset.
856 * This is a bit hackish but saves us from bigger problem.
857 * Maybe it's a good idea to fix this in SDL instead.
859 setenv("SDL_VIDEODRIVER", "x11", 0);
860 #endif
862 flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
863 if (SDL_Init(flags)) {
864 fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
865 SDL_GetError());
866 exit(1);
869 for (i = 0;; i++) {
870 QemuConsole *con = qemu_console_lookup_by_index(i);
871 if (!con) {
872 break;
875 sdl2_num_outputs = i;
876 sdl2_console = g_new0(struct sdl2_state, sdl2_num_outputs);
877 for (i = 0; i < sdl2_num_outputs; i++) {
878 QemuConsole *con = qemu_console_lookup_by_index(i);
879 if (!qemu_console_is_graphic(con)) {
880 sdl2_console[i].hidden = true;
882 sdl2_console[i].dcl.ops = &dcl_ops;
883 sdl2_console[i].dcl.con = con;
884 register_displaychangelistener(&sdl2_console[i].dcl);
885 sdl2_console[i].idx = i;
888 /* Load a 32x32x4 image. White pixels are transparent. */
889 filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp");
890 if (filename) {
891 SDL_Surface *image = SDL_LoadBMP(filename);
892 if (image) {
893 uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255);
894 SDL_SetColorKey(image, SDL_TRUE, colorkey);
895 SDL_SetWindowIcon(sdl2_console[0].real_window, image);
897 g_free(filename);
900 if (full_screen) {
901 gui_fullscreen = 1;
902 sdl_grab_start(0);
905 mouse_mode_notifier.notify = sdl_mouse_mode_change;
906 qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);
908 gui_grab = 0;
910 sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
911 sdl_cursor_normal = SDL_GetCursor();
913 atexit(sdl_cleanup);
915 #endif