]> Git Repo - qemu.git/blame - ui/sdl2.c
vnc-enc-tight: fix Arguments in wrong order
[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>
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"
47c03744
DA
38
39#include "sdl2-keymap.h"
40
41static int sdl2_num_outputs;
42static 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;
363f59d9 51 int hidden;
47c03744
DA
52} *sdl2_console;
53
54static SDL_Surface *guest_sprite_surface;
55static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
56
57static bool gui_saved_scaling;
58static int gui_saved_width;
59static int gui_saved_height;
60static int gui_saved_grab;
61static int gui_fullscreen;
62static int gui_noframe;
63static int gui_key_modifier_pressed;
64static int gui_keysym;
65static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
66static uint8_t modifiers_state[SDL_NUM_SCANCODES];
67static SDL_Cursor *sdl_cursor_normal;
68static SDL_Cursor *sdl_cursor_hidden;
69static int absolute_enabled;
70static int guest_cursor;
71static int guest_x, guest_y;
72static SDL_Cursor *guest_sprite;
73static int scaling_active;
74static Notifier mouse_mode_notifier;
75
76static void sdl_update_caption(struct sdl2_state *scon);
77
78static struct sdl2_state *get_scon_from_window(uint32_t window_id)
79{
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];
84 }
85 }
86 return NULL;
87}
88
89static void sdl_update(DisplayChangeListener *dcl,
90 int x, int y, int w, int h)
91{
92 struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
93 SDL_Rect rect;
94 DisplaySurface *surf = qemu_console_surface(dcl->con);
95
96 if (!surf) {
97 return;
98 }
99 if (!scon->texture) {
100 return;
101 }
102
103 rect.x = x;
104 rect.y = y;
105 rect.w = w;
106 rect.h = h;
107
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);
112}
113
114static void do_sdl_resize(struct sdl2_state *scon, int width, int height,
115 int bpp)
116{
117 int flags;
118
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;
128 }
129 } else {
130 if (!width || !height) {
131 return;
132 }
133 flags = 0;
134 if (gui_fullscreen) {
135 flags |= SDL_WINDOW_FULLSCREEN;
136 } else {
137 flags |= SDL_WINDOW_RESIZABLE;
138 }
363f59d9
GH
139 if (scon->hidden) {
140 flags |= SDL_WINDOW_HIDDEN;
141 }
47c03744
DA
142
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);
148 }
149}
150
151static void sdl_switch(DisplayChangeListener *dcl,
152 DisplaySurface *new_surface)
153{
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;
158
159 /* temporary hack: allows to call sdl_switch to handle scaling changes */
160 if (new_surface) {
161 scon->surface = new_surface;
162 }
163
164 if (!new_surface && idx > 0) {
165 scon->surface = NULL;
166 }
167
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);
173 }
174
175 if (old_surface && scon->texture) {
176 SDL_DestroyTexture(scon->texture);
177 scon->texture = NULL;
178 }
179
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;
186 }
187
188 scon->texture = SDL_CreateTexture(scon->real_renderer, format,
189 SDL_TEXTUREACCESS_STREAMING,
190 surface_width(new_surface),
191 surface_height(new_surface));
192 }
193 }
194}
195
ee8c0b62 196static void reset_keys(struct sdl2_state *scon)
47c03744 197{
ee8c0b62 198 QemuConsole *con = scon ? scon->dcl.con : NULL;
47c03744
DA
199 int i;
200
201 for (i = 0; i < 256; i++) {
202 if (modifiers_state[i]) {
203 int qcode = sdl2_scancode_to_qcode[i];
ee8c0b62 204 qemu_input_event_send_key_qcode(con, qcode, false);
47c03744
DA
205 modifiers_state[i] = 0;
206 }
207 }
208}
209
ee8c0b62
GH
210static void sdl_process_key(struct sdl2_state *scon,
211 SDL_KeyboardEvent *ev)
47c03744
DA
212{
213 int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode];
ee8c0b62 214 QemuConsole *con = scon ? scon->dcl.con : NULL;
47c03744 215
f2335791
GH
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;
228 }
229 }
230 return;
231 }
232
47c03744
DA
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 */
ee8c0b62
GH
238 qemu_input_event_send_key_qcode(con, qcode, true);
239 qemu_input_event_send_key_qcode(con, qcode, false);
47c03744
DA
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;
254 }
255 /* fall though */
256 default:
ee8c0b62 257 qemu_input_event_send_key_qcode(con, qcode,
47c03744
DA
258 ev->type == SDL_KEYDOWN);
259 }
260}
261
262static void sdl_update_caption(struct sdl2_state *scon)
263{
264 char win_title[1024];
265 char icon_title[1024];
266 const char *status = "";
267
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";
277 }
278 }
279
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");
287 }
288
289 if (scon->real_window) {
290 SDL_SetWindowTitle(scon->real_window, win_title);
291 }
292}
293
294static void sdl_hide_cursor(void)
295{
296 if (!cursor_hide) {
297 return;
298 }
299
300 if (qemu_input_is_absolute()) {
301 SDL_ShowCursor(1);
302 SDL_SetCursor(sdl_cursor_hidden);
303 } else {
2d968ffb 304 SDL_SetRelativeMouseMode(SDL_TRUE);
47c03744
DA
305 }
306}
307
308static void sdl_show_cursor(void)
309{
310 if (!cursor_hide) {
311 return;
312 }
313
314 if (!qemu_input_is_absolute()) {
2d968ffb 315 SDL_SetRelativeMouseMode(SDL_FALSE);
47c03744
DA
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);
322 }
323 }
324}
325
326static void sdl_grab_start(struct sdl2_state *scon)
327{
f2335791
GH
328 QemuConsole *con = scon ? scon->dcl.con : NULL;
329
330 if (!con || !qemu_console_is_graphic(con)) {
331 return;
332 }
47c03744
DA
333 /*
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).
337 */
338 if (!(SDL_GetWindowFlags(scon->real_window) & SDL_WINDOW_INPUT_FOCUS)) {
339 return;
340 }
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);
345 }
346 } else {
347 sdl_hide_cursor();
348 }
349 SDL_SetWindowGrab(scon->real_window, SDL_TRUE);
350 gui_grab = 1;
351 sdl_update_caption(scon);
352}
353
354static void sdl_grab_end(struct sdl2_state *scon)
355{
356 SDL_SetWindowGrab(scon->real_window, SDL_FALSE);
357 gui_grab = 0;
358 sdl_show_cursor();
359 sdl_update_caption(scon);
360}
361
362static void absolute_mouse_grab(struct sdl2_state *scon)
363{
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);
371 }
372}
373
374static void sdl_mouse_mode_change(Notifier *notify, void *data)
375{
376 if (qemu_input_is_absolute()) {
377 if (!absolute_enabled) {
378 absolute_enabled = 1;
379 absolute_mouse_grab(&sdl2_console[0]);
380 }
381 } else if (absolute_enabled) {
382 if (!gui_fullscreen) {
383 sdl_grab_end(&sdl2_console[0]);
384 }
385 absolute_enabled = 0;
386 }
387}
388
389static void sdl_send_mouse_event(struct sdl2_state *scon, int dx, int dy,
3f2fde2a 390 int x, int y, int state)
47c03744
DA
391{
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),
47c03744
DA
396 };
397 static uint32_t prev_state;
398
399 if (prev_state != state) {
400 qemu_input_update_buttons(scon->dcl.con, bmap, prev_state, state);
401 prev_state = state;
402 }
403
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;
410
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;
419 }
420 if (scr_h + cur_off_y > max_h) {
421 max_h = scr_h + cur_off_y;
422 }
423 if (i == scon->idx) {
424 off_x = cur_off_x;
425 off_y = cur_off_y;
426 }
427 }
428 }
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);
afbc0dd6
CR
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;
439 }
440 qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_X, dx);
441 qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_Y, dy);
47c03744
DA
442 }
443 qemu_input_event_sync();
444}
445
446static void sdl_scale(struct sdl2_state *scon, int width, int height)
447{
448 int bpp = 0;
449 do_sdl_resize(scon, width, height, bpp);
450 scaling_active = 1;
451}
452
453static void toggle_full_screen(struct sdl2_state *scon)
454{
455 int width = surface_width(scon->surface);
456 int height = surface_height(scon->surface);
457 int bpp = surface_bits_per_pixel(scon->surface);
458
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;
464
465 do_sdl_resize(scon, width, height, bpp);
466 scaling_active = 0;
467
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);
475 }
476 if (!gui_saved_grab) {
477 sdl_grab_end(scon);
478 }
479 }
480 graphic_hw_invalidate(scon->dcl.con);
481 graphic_hw_update(scon->dcl.con);
482}
483
484static void handle_keydown(SDL_Event *ev)
485{
363f59d9 486 int mod_state, win;
47c03744
DA
487 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
488
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;
496 }
497 gui_key_modifier_pressed = mod_state;
498
499 if (gui_key_modifier_pressed) {
500 switch (ev->key.keysym.scancode) {
363f59d9
GH
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);
517 }
518 }
519 gui_keysym = 1;
520 }
521 break;
47c03744
DA
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);
532 }
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);
541
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);
547
548 sdl_scale(scon, width, height);
549 graphic_hw_invalidate(NULL);
550 graphic_hw_update(NULL);
551 gui_keysym = 1;
552 }
553 default:
554 break;
555 }
556 }
557 if (!gui_keysym) {
ee8c0b62 558 sdl_process_key(scon, &ev->key);
47c03744
DA
559 }
560}
561
562static void handle_keyup(SDL_Event *ev)
563{
564 int mod_state;
565 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
566
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));
571 }
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);
580 }
581 /* SDL does not send back all the modifiers key, so we must
582 * correct it. */
ee8c0b62 583 reset_keys(scon);
47c03744
DA
584 return;
585 }
586 gui_keysym = 0;
587 }
588 if (!gui_keysym) {
ee8c0b62 589 sdl_process_key(scon, &ev->key);
47c03744
DA
590 }
591}
592
f2335791
GH
593static void handle_textinput(SDL_Event *ev)
594{
595 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
596 QemuConsole *con = scon ? scon->dcl.con : NULL;
597
598 if (qemu_console_is_graphic(con)) {
599 return;
600 }
601 kbd_put_string_console(con, ev->text.text, strlen(ev->text.text));
602}
603
47c03744
DA
604static void handle_mousemotion(SDL_Event *ev)
605{
606 int max_x, max_y;
607 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
608
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);
617 }
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);
622 }
623 }
624 if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
3f2fde2a 625 sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel,
47c03744
DA
626 ev->motion.x, ev->motion.y, ev->motion.state);
627 }
628}
629
630static void handle_mousebutton(SDL_Event *ev)
631{
632 int buttonstate = SDL_GetMouseState(NULL, NULL);
633 SDL_MouseButtonEvent *bev;
634 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
47c03744
DA
635
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);
641 }
642 } else {
47c03744
DA
643 if (ev->type == SDL_MOUSEBUTTONDOWN) {
644 buttonstate |= SDL_BUTTON(bev->button);
645 } else {
646 buttonstate &= ~SDL_BUTTON(bev->button);
647 }
3f2fde2a
CR
648 sdl_send_mouse_event(scon, 0, 0, bev->x, bev->y, buttonstate);
649 }
650}
651
652static void handle_mousewheel(SDL_Event *ev)
653{
654 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
655 SDL_MouseWheelEvent *wev = &ev->wheel;
656 InputButton btn;
657
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;
47c03744 664 }
3f2fde2a
CR
665
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();
47c03744
DA
670}
671
672static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev)
673{
674 int w, h;
675 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
676
677 switch (ev->window.event) {
678 case SDL_WINDOWEVENT_RESIZED:
679 sdl_scale(scon, ev->window.data1, ev->window.data2);
8b15d9f1
DA
680 {
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);
686 }
47c03744
DA
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);
698 }
699 break;
700 case SDL_WINDOWEVENT_FOCUS_LOST:
701 if (gui_grab && !gui_fullscreen) {
702 sdl_grab_end(scon);
703 }
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();
715 }
716 break;
717 }
718}
719
720static void sdl_refresh(DisplayChangeListener *dcl)
721{
722 struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
723 SDL_Event ev1, *ev = &ev1;
724
725 if (scon->last_vm_running != runstate_is_running()) {
726 scon->last_vm_running = runstate_is_running();
727 sdl_update_caption(scon);
728 }
729
730 graphic_hw_update(dcl->con);
731
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;
f2335791
GH
740 case SDL_TEXTINPUT:
741 handle_textinput(ev);
742 break;
47c03744
DA
743 case SDL_QUIT:
744 if (!no_quit) {
745 no_shutdown = 0;
746 qemu_system_shutdown_request();
747 }
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;
3f2fde2a
CR
756 case SDL_MOUSEWHEEL:
757 handle_mousewheel(ev);
758 break;
47c03744
DA
759 case SDL_WINDOWEVENT:
760 handle_windowevent(dcl, ev);
761 break;
762 default:
763 break;
764 }
765 }
766}
767
768static void sdl_mouse_warp(DisplayChangeListener *dcl,
769 int x, int y, int on)
770{
771 struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
772 if (on) {
773 if (!guest_cursor) {
774 sdl_show_cursor();
775 }
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);
780 }
781 }
782 } else if (gui_grab) {
783 sdl_hide_cursor();
784 }
785 guest_cursor = on;
786 guest_x = x, guest_y = y;
787}
788
789static void sdl_mouse_define(DisplayChangeListener *dcl,
790 QEMUCursor *c)
791{
792
793 if (guest_sprite) {
794 SDL_FreeCursor(guest_sprite);
795 }
796
797 if (guest_sprite_surface) {
798 SDL_FreeSurface(guest_sprite_surface);
799 }
800
801 guest_sprite_surface =
802 SDL_CreateRGBSurfaceFrom(c->data, c->width, c->height, 32, c->width * 4,
803 0xff0000, 0x00ff00, 0xff, 0xff000000);
804
805 if (!guest_sprite_surface) {
806 fprintf(stderr, "Failed to make rgb surface from %p\n", c);
807 return;
808 }
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;
814 }
815 if (guest_cursor &&
816 (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
817 SDL_SetCursor(guest_sprite);
818 }
819}
820
821static void sdl_cleanup(void)
822{
823 if (guest_sprite) {
824 SDL_FreeCursor(guest_sprite);
825 }
826 SDL_QuitSubSystem(SDL_INIT_VIDEO);
827}
828
829static 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,
836};
837
838void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
839{
840 int flags;
841 uint8_t data = 0;
842 char *filename;
843 int i;
844
845 if (no_frame) {
846 gui_noframe = 1;
847 }
848
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.
858 */
859 setenv("SDL_VIDEODRIVER", "x11", 0);
860#endif
861
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);
867 }
868
869 for (i = 0;; i++) {
870 QemuConsole *con = qemu_console_lookup_by_index(i);
f2335791 871 if (!con) {
47c03744
DA
872 break;
873 }
874 }
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);
f2335791
GH
879 if (!qemu_console_is_graphic(con)) {
880 sdl2_console[i].hidden = true;
881 }
47c03744
DA
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;
886 }
887
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);
896 }
897 g_free(filename);
898 }
899
900 if (full_screen) {
901 gui_fullscreen = 1;
902 sdl_grab_start(0);
903 }
904
905 mouse_mode_notifier.notify = sdl_mouse_mode_change;
906 qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);
907
908 gui_grab = 0;
909
910 sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
911 sdl_cursor_normal = SDL_GetCursor();
912
913 atexit(sdl_cleanup);
914}
915#endif
This page took 0.184831 seconds and 4 git commands to generate.