]> Git Repo - qemu.git/blame - ui/sdl2.c
sdl2: overhaul window size handling
[qemu.git] / ui / sdl2.c
CommitLineData
47c03744
DA
1/*
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>
47c03744
DA
30#include <SDL_syswm.h>
31
32#include "qemu-common.h"
33#include "ui/console.h"
34#include "ui/input.h"
5d0fe650 35#include "ui/sdl2.h"
47c03744 36#include "sysemu/sysemu.h"
47c03744 37
47c03744 38static int sdl2_num_outputs;
5d0fe650 39static struct sdl2_console *sdl2_console;
47c03744
DA
40
41static SDL_Surface *guest_sprite_surface;
42static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
43
47c03744
DA
44static int gui_saved_grab;
45static int gui_fullscreen;
46static int gui_noframe;
47static int gui_key_modifier_pressed;
48static int gui_keysym;
49static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
47c03744
DA
50static SDL_Cursor *sdl_cursor_normal;
51static SDL_Cursor *sdl_cursor_hidden;
52static int absolute_enabled;
53static int guest_cursor;
54static int guest_x, guest_y;
55static SDL_Cursor *guest_sprite;
47c03744
DA
56static Notifier mouse_mode_notifier;
57
5d0fe650 58static void sdl_update_caption(struct sdl2_console *scon);
47c03744 59
5d0fe650 60static struct sdl2_console *get_scon_from_window(uint32_t window_id)
47c03744
DA
61{
62 int i;
63 for (i = 0; i < sdl2_num_outputs; i++) {
64 if (sdl2_console[i].real_window == SDL_GetWindowFromID(window_id)) {
65 return &sdl2_console[i];
66 }
67 }
68 return NULL;
69}
70
46522a82 71static void sdl2_window_create(struct sdl2_console *scon)
47c03744 72{
46522a82 73 int flags = 0;
47c03744 74
46522a82
GH
75 if (!scon->surface) {
76 return;
77 }
78 assert(!scon->real_window);
79
80 if (gui_fullscreen) {
81 flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
47c03744 82 } else {
46522a82
GH
83 flags |= SDL_WINDOW_RESIZABLE;
84 }
85 if (scon->hidden) {
86 flags |= SDL_WINDOW_HIDDEN;
87 }
88
89 scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED,
90 SDL_WINDOWPOS_UNDEFINED,
91 surface_width(scon->surface),
92 surface_height(scon->surface),
93 flags);
94 scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0);
95 sdl_update_caption(scon);
96}
47c03744 97
46522a82
GH
98static void sdl2_window_destroy(struct sdl2_console *scon)
99{
100 if (!scon->real_window) {
101 return;
102 }
103
104 SDL_DestroyRenderer(scon->real_renderer);
105 scon->real_renderer = NULL;
106 SDL_DestroyWindow(scon->real_window);
107 scon->real_window = NULL;
108}
109
110static void sdl2_window_resize(struct sdl2_console *scon)
111{
112 if (!scon->real_window) {
113 return;
47c03744 114 }
46522a82
GH
115
116 SDL_SetWindowSize(scon->real_window,
117 surface_width(scon->surface),
118 surface_height(scon->surface));
47c03744
DA
119}
120
121static void sdl_switch(DisplayChangeListener *dcl,
122 DisplaySurface *new_surface)
123{
5d0fe650 124 struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
47c03744 125 DisplaySurface *old_surface = scon->surface;
46522a82 126 int format = 0;
47c03744 127
46522a82 128 scon->surface = new_surface;
47c03744 129
46522a82
GH
130 if (scon->texture) {
131 SDL_DestroyTexture(scon->texture);
132 scon->texture = NULL;
47c03744
DA
133 }
134
46522a82
GH
135 if (!new_surface) {
136 sdl2_window_destroy(scon);
137 return;
47c03744
DA
138 }
139
46522a82
GH
140 if (!scon->real_window) {
141 sdl2_window_create(scon);
142 } else if (old_surface &&
143 ((surface_width(old_surface) != surface_width(new_surface)) ||
144 (surface_height(old_surface) != surface_height(new_surface)))) {
145 sdl2_window_resize(scon);
47c03744
DA
146 }
147
46522a82
GH
148 SDL_RenderSetLogicalSize(scon->real_renderer,
149 surface_width(new_surface),
150 surface_height(new_surface));
47c03744 151
46522a82
GH
152 if (surface_bits_per_pixel(scon->surface) == 16) {
153 format = SDL_PIXELFORMAT_RGB565;
154 } else if (surface_bits_per_pixel(scon->surface) == 32) {
155 format = SDL_PIXELFORMAT_ARGB8888;
47c03744 156 }
46522a82
GH
157 scon->texture = SDL_CreateTexture(scon->real_renderer, format,
158 SDL_TEXTUREACCESS_STREAMING,
159 surface_width(new_surface),
160 surface_height(new_surface));
47c03744
DA
161}
162
5d0fe650 163static void sdl_update_caption(struct sdl2_console *scon)
47c03744
DA
164{
165 char win_title[1024];
166 char icon_title[1024];
167 const char *status = "";
168
169 if (!runstate_is_running()) {
170 status = " [Stopped]";
171 } else if (gui_grab) {
172 if (alt_grab) {
44f017d0 173 status = " - Press Ctrl-Alt-Shift to exit grab";
47c03744 174 } else if (ctrl_grab) {
44f017d0 175 status = " - Press Right-Ctrl to exit grab";
47c03744 176 } else {
44f017d0 177 status = " - Press Ctrl-Alt to exit grab";
47c03744
DA
178 }
179 }
180
181 if (qemu_name) {
182 snprintf(win_title, sizeof(win_title), "QEMU (%s-%d)%s", qemu_name,
183 scon->idx, status);
184 snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name);
185 } else {
186 snprintf(win_title, sizeof(win_title), "QEMU%s", status);
187 snprintf(icon_title, sizeof(icon_title), "QEMU");
188 }
189
190 if (scon->real_window) {
191 SDL_SetWindowTitle(scon->real_window, win_title);
192 }
193}
194
195static void sdl_hide_cursor(void)
196{
197 if (!cursor_hide) {
198 return;
199 }
200
201 if (qemu_input_is_absolute()) {
202 SDL_ShowCursor(1);
203 SDL_SetCursor(sdl_cursor_hidden);
204 } else {
2d968ffb 205 SDL_SetRelativeMouseMode(SDL_TRUE);
47c03744
DA
206 }
207}
208
209static void sdl_show_cursor(void)
210{
211 if (!cursor_hide) {
212 return;
213 }
214
215 if (!qemu_input_is_absolute()) {
2d968ffb 216 SDL_SetRelativeMouseMode(SDL_FALSE);
47c03744
DA
217 SDL_ShowCursor(1);
218 if (guest_cursor &&
219 (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
220 SDL_SetCursor(guest_sprite);
221 } else {
222 SDL_SetCursor(sdl_cursor_normal);
223 }
224 }
225}
226
5d0fe650 227static void sdl_grab_start(struct sdl2_console *scon)
47c03744 228{
f2335791
GH
229 QemuConsole *con = scon ? scon->dcl.con : NULL;
230
231 if (!con || !qemu_console_is_graphic(con)) {
232 return;
233 }
47c03744
DA
234 /*
235 * If the application is not active, do not try to enter grab state. This
236 * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the
237 * application (SDL bug).
238 */
239 if (!(SDL_GetWindowFlags(scon->real_window) & SDL_WINDOW_INPUT_FOCUS)) {
240 return;
241 }
242 if (guest_cursor) {
243 SDL_SetCursor(guest_sprite);
244 if (!qemu_input_is_absolute() && !absolute_enabled) {
245 SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y);
246 }
247 } else {
248 sdl_hide_cursor();
249 }
250 SDL_SetWindowGrab(scon->real_window, SDL_TRUE);
251 gui_grab = 1;
252 sdl_update_caption(scon);
253}
254
5d0fe650 255static void sdl_grab_end(struct sdl2_console *scon)
47c03744
DA
256{
257 SDL_SetWindowGrab(scon->real_window, SDL_FALSE);
258 gui_grab = 0;
259 sdl_show_cursor();
260 sdl_update_caption(scon);
261}
262
5d0fe650 263static void absolute_mouse_grab(struct sdl2_console *scon)
47c03744
DA
264{
265 int mouse_x, mouse_y;
266 int scr_w, scr_h;
267 SDL_GetMouseState(&mouse_x, &mouse_y);
268 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
269 if (mouse_x > 0 && mouse_x < scr_w - 1 &&
270 mouse_y > 0 && mouse_y < scr_h - 1) {
271 sdl_grab_start(scon);
272 }
273}
274
275static void sdl_mouse_mode_change(Notifier *notify, void *data)
276{
277 if (qemu_input_is_absolute()) {
278 if (!absolute_enabled) {
279 absolute_enabled = 1;
280 absolute_mouse_grab(&sdl2_console[0]);
281 }
282 } else if (absolute_enabled) {
283 if (!gui_fullscreen) {
284 sdl_grab_end(&sdl2_console[0]);
285 }
286 absolute_enabled = 0;
287 }
288}
289
5d0fe650 290static void sdl_send_mouse_event(struct sdl2_console *scon, int dx, int dy,
3f2fde2a 291 int x, int y, int state)
47c03744
DA
292{
293 static uint32_t bmap[INPUT_BUTTON_MAX] = {
294 [INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT),
295 [INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE),
296 [INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT),
47c03744
DA
297 };
298 static uint32_t prev_state;
299
300 if (prev_state != state) {
301 qemu_input_update_buttons(scon->dcl.con, bmap, prev_state, state);
302 prev_state = state;
303 }
304
305 if (qemu_input_is_absolute()) {
306 int scr_w, scr_h;
307 int max_w = 0, max_h = 0;
308 int off_x = 0, off_y = 0;
309 int cur_off_x = 0, cur_off_y = 0;
310 int i;
311
312 for (i = 0; i < sdl2_num_outputs; i++) {
5d0fe650 313 struct sdl2_console *thiscon = &sdl2_console[i];
47c03744
DA
314 if (thiscon->real_window && thiscon->surface) {
315 SDL_GetWindowSize(thiscon->real_window, &scr_w, &scr_h);
316 cur_off_x = thiscon->x;
317 cur_off_y = thiscon->y;
318 if (scr_w + cur_off_x > max_w) {
319 max_w = scr_w + cur_off_x;
320 }
321 if (scr_h + cur_off_y > max_h) {
322 max_h = scr_h + cur_off_y;
323 }
324 if (i == scon->idx) {
325 off_x = cur_off_x;
326 off_y = cur_off_y;
327 }
328 }
329 }
330 qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, max_w);
331 qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, off_y + y, max_h);
afbc0dd6
CR
332 } else {
333 if (guest_cursor) {
334 x -= guest_x;
335 y -= guest_y;
336 guest_x += x;
337 guest_y += y;
338 dx = x;
339 dy = y;
340 }
341 qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_X, dx);
342 qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_Y, dy);
47c03744
DA
343 }
344 qemu_input_event_sync();
345}
346
5d0fe650 347static void toggle_full_screen(struct sdl2_console *scon)
47c03744 348{
47c03744
DA
349 gui_fullscreen = !gui_fullscreen;
350 if (gui_fullscreen) {
46522a82
GH
351 SDL_SetWindowFullscreen(scon->real_window,
352 SDL_WINDOW_FULLSCREEN_DESKTOP);
47c03744
DA
353 gui_saved_grab = gui_grab;
354 sdl_grab_start(scon);
355 } else {
47c03744
DA
356 if (!gui_saved_grab) {
357 sdl_grab_end(scon);
358 }
46522a82 359 SDL_SetWindowFullscreen(scon->real_window, 0);
47c03744
DA
360 }
361 graphic_hw_invalidate(scon->dcl.con);
362 graphic_hw_update(scon->dcl.con);
363}
364
365static void handle_keydown(SDL_Event *ev)
366{
363f59d9 367 int mod_state, win;
5d0fe650 368 struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
47c03744
DA
369
370 if (alt_grab) {
371 mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) ==
372 (gui_grab_code | KMOD_LSHIFT);
373 } else if (ctrl_grab) {
374 mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL;
375 } else {
376 mod_state = (SDL_GetModState() & gui_grab_code) == gui_grab_code;
377 }
378 gui_key_modifier_pressed = mod_state;
379
380 if (gui_key_modifier_pressed) {
381 switch (ev->key.keysym.scancode) {
363f59d9
GH
382 case SDL_SCANCODE_2:
383 case SDL_SCANCODE_3:
384 case SDL_SCANCODE_4:
385 case SDL_SCANCODE_5:
386 case SDL_SCANCODE_6:
387 case SDL_SCANCODE_7:
388 case SDL_SCANCODE_8:
389 case SDL_SCANCODE_9:
390 win = ev->key.keysym.scancode - SDL_SCANCODE_1;
391 if (win < sdl2_num_outputs) {
392 sdl2_console[win].hidden = !sdl2_console[win].hidden;
393 if (sdl2_console[win].real_window) {
394 if (sdl2_console[win].hidden) {
395 SDL_HideWindow(sdl2_console[win].real_window);
396 } else {
397 SDL_ShowWindow(sdl2_console[win].real_window);
398 }
399 }
400 gui_keysym = 1;
401 }
402 break;
47c03744
DA
403 case SDL_SCANCODE_F:
404 toggle_full_screen(scon);
405 gui_keysym = 1;
406 break;
407 case SDL_SCANCODE_U:
46522a82
GH
408 sdl2_window_destroy(scon);
409 sdl2_window_create(scon);
410 graphic_hw_invalidate(scon->dcl.con);
411 graphic_hw_update(scon->dcl.con);
47c03744
DA
412 gui_keysym = 1;
413 break;
46522a82 414#if 0
47c03744
DA
415 case SDL_SCANCODE_KP_PLUS:
416 case SDL_SCANCODE_KP_MINUS:
417 if (!gui_fullscreen) {
418 int scr_w, scr_h;
419 int width, height;
420 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
421
422 width = MAX(scr_w + (ev->key.keysym.scancode ==
423 SDL_SCANCODE_KP_PLUS ? 50 : -50),
424 160);
425 height = (surface_height(scon->surface) * width) /
426 surface_width(scon->surface);
46522a82
GH
427 fprintf(stderr, "%s: scale to %dx%d\n",
428 __func__, width, height);
47c03744
DA
429 sdl_scale(scon, width, height);
430 graphic_hw_invalidate(NULL);
431 graphic_hw_update(NULL);
432 gui_keysym = 1;
433 }
46522a82 434#endif
47c03744
DA
435 default:
436 break;
437 }
438 }
439 if (!gui_keysym) {
8fc1a3f5 440 sdl2_process_key(scon, &ev->key);
47c03744
DA
441 }
442}
443
444static void handle_keyup(SDL_Event *ev)
445{
446 int mod_state;
5d0fe650 447 struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
47c03744
DA
448
449 if (!alt_grab) {
450 mod_state = (ev->key.keysym.mod & gui_grab_code);
451 } else {
452 mod_state = (ev->key.keysym.mod & (gui_grab_code | KMOD_LSHIFT));
453 }
454 if (!mod_state && gui_key_modifier_pressed) {
455 gui_key_modifier_pressed = 0;
456 if (gui_keysym == 0) {
457 /* exit/enter grab if pressing Ctrl-Alt */
458 if (!gui_grab) {
459 sdl_grab_start(scon);
460 } else if (!gui_fullscreen) {
461 sdl_grab_end(scon);
462 }
463 /* SDL does not send back all the modifiers key, so we must
464 * correct it. */
8fc1a3f5 465 sdl2_reset_keys(scon);
47c03744
DA
466 return;
467 }
468 gui_keysym = 0;
469 }
470 if (!gui_keysym) {
8fc1a3f5 471 sdl2_process_key(scon, &ev->key);
47c03744
DA
472 }
473}
474
f2335791
GH
475static void handle_textinput(SDL_Event *ev)
476{
5d0fe650 477 struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
f2335791
GH
478 QemuConsole *con = scon ? scon->dcl.con : NULL;
479
480 if (qemu_console_is_graphic(con)) {
481 return;
482 }
483 kbd_put_string_console(con, ev->text.text, strlen(ev->text.text));
484}
485
47c03744
DA
486static void handle_mousemotion(SDL_Event *ev)
487{
488 int max_x, max_y;
5d0fe650 489 struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
47c03744
DA
490
491 if (qemu_input_is_absolute() || absolute_enabled) {
492 int scr_w, scr_h;
493 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
494 max_x = scr_w - 1;
495 max_y = scr_h - 1;
496 if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 ||
497 ev->motion.x == max_x || ev->motion.y == max_y)) {
498 sdl_grab_end(scon);
499 }
500 if (!gui_grab &&
501 (ev->motion.x > 0 && ev->motion.x < max_x &&
502 ev->motion.y > 0 && ev->motion.y < max_y)) {
503 sdl_grab_start(scon);
504 }
505 }
506 if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
3f2fde2a 507 sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel,
47c03744
DA
508 ev->motion.x, ev->motion.y, ev->motion.state);
509 }
510}
511
512static void handle_mousebutton(SDL_Event *ev)
513{
514 int buttonstate = SDL_GetMouseState(NULL, NULL);
515 SDL_MouseButtonEvent *bev;
5d0fe650 516 struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
47c03744
DA
517
518 bev = &ev->button;
519 if (!gui_grab && !qemu_input_is_absolute()) {
520 if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
521 /* start grabbing all events */
522 sdl_grab_start(scon);
523 }
524 } else {
47c03744
DA
525 if (ev->type == SDL_MOUSEBUTTONDOWN) {
526 buttonstate |= SDL_BUTTON(bev->button);
527 } else {
528 buttonstate &= ~SDL_BUTTON(bev->button);
529 }
3f2fde2a
CR
530 sdl_send_mouse_event(scon, 0, 0, bev->x, bev->y, buttonstate);
531 }
532}
533
534static void handle_mousewheel(SDL_Event *ev)
535{
5d0fe650 536 struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
3f2fde2a
CR
537 SDL_MouseWheelEvent *wev = &ev->wheel;
538 InputButton btn;
539
540 if (wev->y > 0) {
541 btn = INPUT_BUTTON_WHEEL_UP;
542 } else if (wev->y < 0) {
543 btn = INPUT_BUTTON_WHEEL_DOWN;
544 } else {
545 return;
47c03744 546 }
3f2fde2a
CR
547
548 qemu_input_queue_btn(scon->dcl.con, btn, true);
549 qemu_input_event_sync();
550 qemu_input_queue_btn(scon->dcl.con, btn, false);
551 qemu_input_event_sync();
47c03744
DA
552}
553
554static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev)
555{
556 int w, h;
5d0fe650 557 struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
47c03744
DA
558
559 switch (ev->window.event) {
560 case SDL_WINDOWEVENT_RESIZED:
8b15d9f1
DA
561 {
562 QemuUIInfo info;
563 memset(&info, 0, sizeof(info));
564 info.width = ev->window.data1;
565 info.height = ev->window.data2;
566 dpy_set_ui_info(scon->dcl.con, &info);
567 }
47c03744
DA
568 graphic_hw_invalidate(scon->dcl.con);
569 graphic_hw_update(scon->dcl.con);
570 break;
571 case SDL_WINDOWEVENT_EXPOSED:
572 SDL_GetWindowSize(SDL_GetWindowFromID(ev->window.windowID), &w, &h);
f1ddebd8 573 sdl2_2d_update(dcl, 0, 0, w, h);
47c03744
DA
574 break;
575 case SDL_WINDOWEVENT_FOCUS_GAINED:
576 case SDL_WINDOWEVENT_ENTER:
577 if (!gui_grab && (qemu_input_is_absolute() || absolute_enabled)) {
578 absolute_mouse_grab(scon);
579 }
580 break;
581 case SDL_WINDOWEVENT_FOCUS_LOST:
582 if (gui_grab && !gui_fullscreen) {
583 sdl_grab_end(scon);
584 }
585 break;
586 case SDL_WINDOWEVENT_RESTORED:
587 update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT);
588 break;
589 case SDL_WINDOWEVENT_MINIMIZED:
590 update_displaychangelistener(dcl, 500);
591 break;
592 case SDL_WINDOWEVENT_CLOSE:
593 if (!no_quit) {
594 no_shutdown = 0;
595 qemu_system_shutdown_request();
596 }
597 break;
598 }
599}
600
601static void sdl_refresh(DisplayChangeListener *dcl)
602{
5d0fe650 603 struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
47c03744
DA
604 SDL_Event ev1, *ev = &ev1;
605
606 if (scon->last_vm_running != runstate_is_running()) {
607 scon->last_vm_running = runstate_is_running();
608 sdl_update_caption(scon);
609 }
610
611 graphic_hw_update(dcl->con);
612
613 while (SDL_PollEvent(ev)) {
614 switch (ev->type) {
615 case SDL_KEYDOWN:
616 handle_keydown(ev);
617 break;
618 case SDL_KEYUP:
619 handle_keyup(ev);
620 break;
f2335791
GH
621 case SDL_TEXTINPUT:
622 handle_textinput(ev);
623 break;
47c03744
DA
624 case SDL_QUIT:
625 if (!no_quit) {
626 no_shutdown = 0;
627 qemu_system_shutdown_request();
628 }
629 break;
630 case SDL_MOUSEMOTION:
631 handle_mousemotion(ev);
632 break;
633 case SDL_MOUSEBUTTONDOWN:
634 case SDL_MOUSEBUTTONUP:
635 handle_mousebutton(ev);
636 break;
3f2fde2a
CR
637 case SDL_MOUSEWHEEL:
638 handle_mousewheel(ev);
639 break;
47c03744
DA
640 case SDL_WINDOWEVENT:
641 handle_windowevent(dcl, ev);
642 break;
643 default:
644 break;
645 }
646 }
647}
648
649static void sdl_mouse_warp(DisplayChangeListener *dcl,
650 int x, int y, int on)
651{
5d0fe650 652 struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
47c03744
DA
653 if (on) {
654 if (!guest_cursor) {
655 sdl_show_cursor();
656 }
657 if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
658 SDL_SetCursor(guest_sprite);
659 if (!qemu_input_is_absolute() && !absolute_enabled) {
660 SDL_WarpMouseInWindow(scon->real_window, x, y);
661 }
662 }
663 } else if (gui_grab) {
664 sdl_hide_cursor();
665 }
666 guest_cursor = on;
667 guest_x = x, guest_y = y;
668}
669
670static void sdl_mouse_define(DisplayChangeListener *dcl,
671 QEMUCursor *c)
672{
673
674 if (guest_sprite) {
675 SDL_FreeCursor(guest_sprite);
676 }
677
678 if (guest_sprite_surface) {
679 SDL_FreeSurface(guest_sprite_surface);
680 }
681
682 guest_sprite_surface =
683 SDL_CreateRGBSurfaceFrom(c->data, c->width, c->height, 32, c->width * 4,
684 0xff0000, 0x00ff00, 0xff, 0xff000000);
685
686 if (!guest_sprite_surface) {
687 fprintf(stderr, "Failed to make rgb surface from %p\n", c);
688 return;
689 }
690 guest_sprite = SDL_CreateColorCursor(guest_sprite_surface,
691 c->hot_x, c->hot_y);
692 if (!guest_sprite) {
693 fprintf(stderr, "Failed to make color cursor from %p\n", c);
694 return;
695 }
696 if (guest_cursor &&
697 (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
698 SDL_SetCursor(guest_sprite);
699 }
700}
701
702static void sdl_cleanup(void)
703{
704 if (guest_sprite) {
705 SDL_FreeCursor(guest_sprite);
706 }
707 SDL_QuitSubSystem(SDL_INIT_VIDEO);
708}
709
f1ddebd8
GH
710static const DisplayChangeListenerOps dcl_2d_ops = {
711 .dpy_name = "sdl2-2d",
712 .dpy_gfx_update = sdl2_2d_update,
47c03744
DA
713 .dpy_gfx_switch = sdl_switch,
714 .dpy_refresh = sdl_refresh,
715 .dpy_mouse_set = sdl_mouse_warp,
716 .dpy_cursor_define = sdl_mouse_define,
717};
718
719void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
720{
721 int flags;
722 uint8_t data = 0;
723 char *filename;
724 int i;
725
726 if (no_frame) {
727 gui_noframe = 1;
728 }
729
730#ifdef __linux__
731 /* on Linux, SDL may use fbcon|directfb|svgalib when run without
732 * accessible $DISPLAY to open X11 window. This is often the case
733 * when qemu is run using sudo. But in this case, and when actually
734 * run in X11 environment, SDL fights with X11 for the video card,
735 * making current display unavailable, often until reboot.
736 * So make x11 the default SDL video driver if this variable is unset.
737 * This is a bit hackish but saves us from bigger problem.
738 * Maybe it's a good idea to fix this in SDL instead.
739 */
740 setenv("SDL_VIDEODRIVER", "x11", 0);
741#endif
742
743 flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
744 if (SDL_Init(flags)) {
745 fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
746 SDL_GetError());
747 exit(1);
748 }
44f017d0 749 SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "1");
47c03744
DA
750
751 for (i = 0;; i++) {
752 QemuConsole *con = qemu_console_lookup_by_index(i);
f2335791 753 if (!con) {
47c03744
DA
754 break;
755 }
756 }
757 sdl2_num_outputs = i;
5d0fe650 758 sdl2_console = g_new0(struct sdl2_console, sdl2_num_outputs);
47c03744
DA
759 for (i = 0; i < sdl2_num_outputs; i++) {
760 QemuConsole *con = qemu_console_lookup_by_index(i);
f2335791
GH
761 if (!qemu_console_is_graphic(con)) {
762 sdl2_console[i].hidden = true;
763 }
f1ddebd8 764 sdl2_console[i].dcl.ops = &dcl_2d_ops;
47c03744
DA
765 sdl2_console[i].dcl.con = con;
766 register_displaychangelistener(&sdl2_console[i].dcl);
767 sdl2_console[i].idx = i;
768 }
769
770 /* Load a 32x32x4 image. White pixels are transparent. */
771 filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp");
772 if (filename) {
773 SDL_Surface *image = SDL_LoadBMP(filename);
774 if (image) {
775 uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255);
776 SDL_SetColorKey(image, SDL_TRUE, colorkey);
777 SDL_SetWindowIcon(sdl2_console[0].real_window, image);
778 }
779 g_free(filename);
780 }
781
782 if (full_screen) {
783 gui_fullscreen = 1;
784 sdl_grab_start(0);
785 }
786
787 mouse_mode_notifier.notify = sdl_mouse_mode_change;
788 qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);
789
790 gui_grab = 0;
791
792 sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
793 sdl_cursor_normal = SDL_GetCursor();
794
795 atexit(sdl_cleanup);
796}
This page took 0.137573 seconds and 4 git commands to generate.