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