]>
Commit | Line | Data |
---|---|---|
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 | ||
e16f4c87 | 26 | #include "qemu/osdep.h" |
0b8fa32f | 27 | #include "qemu/module.h" |
77d910fb | 28 | #include "qemu/cutils.h" |
47c03744 DA |
29 | #include "ui/console.h" |
30 | #include "ui/input.h" | |
5d0fe650 | 31 | #include "ui/sdl2.h" |
54d31236 | 32 | #include "sysemu/runstate.h" |
e6dba048 | 33 | #include "sysemu/runstate-action.h" |
47c03744 | 34 | #include "sysemu/sysemu.h" |
83047345 | 35 | #include "ui/win32-kbd-hook.h" |
ed80f503 | 36 | #include "qemu/log.h" |
47c03744 | 37 | |
47c03744 | 38 | static int sdl2_num_outputs; |
5d0fe650 | 39 | static struct sdl2_console *sdl2_console; |
47c03744 DA |
40 | |
41 | static SDL_Surface *guest_sprite_surface; | |
42 | static int gui_grab; /* if true, all keyboard/mouse events are grabbed */ | |
9eafdeea TH |
43 | static bool alt_grab; |
44 | static bool ctrl_grab; | |
47c03744 | 45 | |
47c03744 DA |
46 | static int gui_saved_grab; |
47 | static int gui_fullscreen; | |
47c03744 | 48 | static int gui_grab_code = KMOD_LALT | KMOD_LCTRL; |
47c03744 DA |
49 | static SDL_Cursor *sdl_cursor_normal; |
50 | static SDL_Cursor *sdl_cursor_hidden; | |
51 | static int absolute_enabled; | |
52 | static int guest_cursor; | |
53 | static int guest_x, guest_y; | |
54 | static SDL_Cursor *guest_sprite; | |
47c03744 DA |
55 | static Notifier mouse_mode_notifier; |
56 | ||
56bdd4b6 JM |
57 | #define SDL2_REFRESH_INTERVAL_BUSY 10 |
58 | #define SDL2_MAX_IDLE_COUNT (2 * GUI_REFRESH_INTERVAL_DEFAULT \ | |
59 | / SDL2_REFRESH_INTERVAL_BUSY + 1) | |
60 | ||
da3f7a3a MAL |
61 | /* introduced in SDL 2.0.10 */ |
62 | #ifndef SDL_HINT_RENDER_BATCHING | |
63 | #define SDL_HINT_RENDER_BATCHING "SDL_RENDER_BATCHING" | |
64 | #endif | |
65 | ||
5d0fe650 | 66 | static void sdl_update_caption(struct sdl2_console *scon); |
47c03744 | 67 | |
5d0fe650 | 68 | static struct sdl2_console *get_scon_from_window(uint32_t window_id) |
47c03744 DA |
69 | { |
70 | int i; | |
71 | for (i = 0; i < sdl2_num_outputs; i++) { | |
72 | if (sdl2_console[i].real_window == SDL_GetWindowFromID(window_id)) { | |
73 | return &sdl2_console[i]; | |
74 | } | |
75 | } | |
76 | return NULL; | |
77 | } | |
78 | ||
2c3056f1 | 79 | void sdl2_window_create(struct sdl2_console *scon) |
47c03744 | 80 | { |
46522a82 | 81 | int flags = 0; |
47c03744 | 82 | |
46522a82 GH |
83 | if (!scon->surface) { |
84 | return; | |
85 | } | |
86 | assert(!scon->real_window); | |
87 | ||
88 | if (gui_fullscreen) { | |
89 | flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; | |
47c03744 | 90 | } else { |
46522a82 GH |
91 | flags |= SDL_WINDOW_RESIZABLE; |
92 | } | |
93 | if (scon->hidden) { | |
94 | flags |= SDL_WINDOW_HIDDEN; | |
95 | } | |
67c6f1db JHW |
96 | #ifdef CONFIG_OPENGL |
97 | if (scon->opengl) { | |
98 | flags |= SDL_WINDOW_OPENGL; | |
99 | } | |
100 | #endif | |
46522a82 GH |
101 | |
102 | scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, | |
103 | SDL_WINDOWPOS_UNDEFINED, | |
104 | surface_width(scon->surface), | |
105 | surface_height(scon->surface), | |
106 | flags); | |
da3f7a3a MAL |
107 | if (scon->opengl) { |
108 | const char *driver = "opengl"; | |
109 | ||
110 | if (scon->opts->gl == DISPLAYGL_MODE_ES) { | |
111 | driver = "opengles2"; | |
112 | } | |
113 | ||
114 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, driver); | |
115 | SDL_SetHint(SDL_HINT_RENDER_BATCHING, "1"); | |
da3f7a3a | 116 | |
6f2688f7 | 117 | scon->winctx = SDL_GL_CreateContext(scon->real_window); |
176e3783 AC |
118 | } else { |
119 | /* The SDL renderer is only used by sdl2-2D, when OpenGL is disabled */ | |
120 | scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0); | |
0b71a5d5 | 121 | } |
46522a82 GH |
122 | sdl_update_caption(scon); |
123 | } | |
47c03744 | 124 | |
2c3056f1 | 125 | void sdl2_window_destroy(struct sdl2_console *scon) |
46522a82 GH |
126 | { |
127 | if (!scon->real_window) { | |
128 | return; | |
129 | } | |
130 | ||
176e3783 AC |
131 | if (scon->winctx) { |
132 | SDL_GL_DeleteContext(scon->winctx); | |
133 | scon->winctx = NULL; | |
134 | } | |
135 | if (scon->real_renderer) { | |
136 | SDL_DestroyRenderer(scon->real_renderer); | |
137 | scon->real_renderer = NULL; | |
138 | } | |
46522a82 GH |
139 | SDL_DestroyWindow(scon->real_window); |
140 | scon->real_window = NULL; | |
141 | } | |
142 | ||
2c3056f1 | 143 | void sdl2_window_resize(struct sdl2_console *scon) |
46522a82 GH |
144 | { |
145 | if (!scon->real_window) { | |
146 | return; | |
47c03744 | 147 | } |
46522a82 GH |
148 | |
149 | SDL_SetWindowSize(scon->real_window, | |
150 | surface_width(scon->surface), | |
151 | surface_height(scon->surface)); | |
47c03744 DA |
152 | } |
153 | ||
0b71a5d5 GH |
154 | static void sdl2_redraw(struct sdl2_console *scon) |
155 | { | |
156 | if (scon->opengl) { | |
157 | #ifdef CONFIG_OPENGL | |
158 | sdl2_gl_redraw(scon); | |
159 | #endif | |
160 | } else { | |
161 | sdl2_2d_redraw(scon); | |
162 | } | |
163 | } | |
164 | ||
5d0fe650 | 165 | static void sdl_update_caption(struct sdl2_console *scon) |
47c03744 DA |
166 | { |
167 | char win_title[1024]; | |
168 | char icon_title[1024]; | |
169 | const char *status = ""; | |
170 | ||
171 | if (!runstate_is_running()) { | |
172 | status = " [Stopped]"; | |
173 | } else if (gui_grab) { | |
174 | if (alt_grab) { | |
547ec5a0 AW |
175 | #ifdef CONFIG_DARWIN |
176 | status = " - Press ⌃⌥⇧G to exit grab"; | |
177 | #else | |
f8d2c936 | 178 | status = " - Press Ctrl-Alt-Shift-G to exit grab"; |
547ec5a0 | 179 | #endif |
47c03744 | 180 | } else if (ctrl_grab) { |
f8d2c936 | 181 | status = " - Press Right-Ctrl-G to exit grab"; |
47c03744 | 182 | } else { |
547ec5a0 AW |
183 | #ifdef CONFIG_DARWIN |
184 | status = " - Press ⌃⌥G to exit grab"; | |
185 | #else | |
f8d2c936 | 186 | status = " - Press Ctrl-Alt-G to exit grab"; |
547ec5a0 | 187 | #endif |
47c03744 DA |
188 | } |
189 | } | |
190 | ||
191 | if (qemu_name) { | |
192 | snprintf(win_title, sizeof(win_title), "QEMU (%s-%d)%s", qemu_name, | |
193 | scon->idx, status); | |
194 | snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name); | |
195 | } else { | |
196 | snprintf(win_title, sizeof(win_title), "QEMU%s", status); | |
197 | snprintf(icon_title, sizeof(icon_title), "QEMU"); | |
198 | } | |
199 | ||
200 | if (scon->real_window) { | |
201 | SDL_SetWindowTitle(scon->real_window, win_title); | |
202 | } | |
203 | } | |
204 | ||
86a088e6 | 205 | static void sdl_hide_cursor(struct sdl2_console *scon) |
47c03744 | 206 | { |
86a088e6 | 207 | if (scon->opts->has_show_cursor && scon->opts->show_cursor) { |
47c03744 DA |
208 | return; |
209 | } | |
210 | ||
253347e1 JM |
211 | SDL_ShowCursor(SDL_DISABLE); |
212 | SDL_SetCursor(sdl_cursor_hidden); | |
213 | ||
0337e412 | 214 | if (!qemu_input_is_absolute(scon->dcl.con)) { |
2d968ffb | 215 | SDL_SetRelativeMouseMode(SDL_TRUE); |
47c03744 DA |
216 | } |
217 | } | |
218 | ||
86a088e6 | 219 | static void sdl_show_cursor(struct sdl2_console *scon) |
47c03744 | 220 | { |
86a088e6 | 221 | if (scon->opts->has_show_cursor && scon->opts->show_cursor) { |
47c03744 DA |
222 | return; |
223 | } | |
224 | ||
0337e412 | 225 | if (!qemu_input_is_absolute(scon->dcl.con)) { |
2d968ffb | 226 | SDL_SetRelativeMouseMode(SDL_FALSE); |
47c03744 | 227 | } |
253347e1 JM |
228 | |
229 | if (guest_cursor && | |
0337e412 | 230 | (gui_grab || qemu_input_is_absolute(scon->dcl.con) || absolute_enabled)) { |
253347e1 JM |
231 | SDL_SetCursor(guest_sprite); |
232 | } else { | |
233 | SDL_SetCursor(sdl_cursor_normal); | |
234 | } | |
235 | ||
236 | SDL_ShowCursor(SDL_ENABLE); | |
47c03744 DA |
237 | } |
238 | ||
5d0fe650 | 239 | static void sdl_grab_start(struct sdl2_console *scon) |
47c03744 | 240 | { |
f2335791 GH |
241 | QemuConsole *con = scon ? scon->dcl.con : NULL; |
242 | ||
243 | if (!con || !qemu_console_is_graphic(con)) { | |
244 | return; | |
245 | } | |
47c03744 DA |
246 | /* |
247 | * If the application is not active, do not try to enter grab state. This | |
248 | * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the | |
249 | * application (SDL bug). | |
250 | */ | |
251 | if (!(SDL_GetWindowFlags(scon->real_window) & SDL_WINDOW_INPUT_FOCUS)) { | |
252 | return; | |
253 | } | |
254 | if (guest_cursor) { | |
255 | SDL_SetCursor(guest_sprite); | |
0337e412 | 256 | if (!qemu_input_is_absolute(scon->dcl.con) && !absolute_enabled) { |
47c03744 DA |
257 | SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y); |
258 | } | |
259 | } else { | |
86a088e6 | 260 | sdl_hide_cursor(scon); |
47c03744 DA |
261 | } |
262 | SDL_SetWindowGrab(scon->real_window, SDL_TRUE); | |
263 | gui_grab = 1; | |
83047345 | 264 | win32_kbd_set_grab(true); |
47c03744 DA |
265 | sdl_update_caption(scon); |
266 | } | |
267 | ||
5d0fe650 | 268 | static void sdl_grab_end(struct sdl2_console *scon) |
47c03744 DA |
269 | { |
270 | SDL_SetWindowGrab(scon->real_window, SDL_FALSE); | |
271 | gui_grab = 0; | |
83047345 | 272 | win32_kbd_set_grab(false); |
86a088e6 | 273 | sdl_show_cursor(scon); |
47c03744 DA |
274 | sdl_update_caption(scon); |
275 | } | |
276 | ||
5d0fe650 | 277 | static void absolute_mouse_grab(struct sdl2_console *scon) |
47c03744 DA |
278 | { |
279 | int mouse_x, mouse_y; | |
280 | int scr_w, scr_h; | |
281 | SDL_GetMouseState(&mouse_x, &mouse_y); | |
282 | SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); | |
283 | if (mouse_x > 0 && mouse_x < scr_w - 1 && | |
284 | mouse_y > 0 && mouse_y < scr_h - 1) { | |
285 | sdl_grab_start(scon); | |
286 | } | |
287 | } | |
288 | ||
289 | static void sdl_mouse_mode_change(Notifier *notify, void *data) | |
290 | { | |
0337e412 | 291 | if (qemu_input_is_absolute(sdl2_console[0].dcl.con)) { |
47c03744 DA |
292 | if (!absolute_enabled) { |
293 | absolute_enabled = 1; | |
8dfa3061 | 294 | SDL_SetRelativeMouseMode(SDL_FALSE); |
47c03744 DA |
295 | absolute_mouse_grab(&sdl2_console[0]); |
296 | } | |
297 | } else if (absolute_enabled) { | |
298 | if (!gui_fullscreen) { | |
299 | sdl_grab_end(&sdl2_console[0]); | |
300 | } | |
301 | absolute_enabled = 0; | |
302 | } | |
303 | } | |
304 | ||
5d0fe650 | 305 | static void sdl_send_mouse_event(struct sdl2_console *scon, int dx, int dy, |
3f2fde2a | 306 | int x, int y, int state) |
47c03744 | 307 | { |
7fb1cf16 | 308 | static uint32_t bmap[INPUT_BUTTON__MAX] = { |
47c03744 DA |
309 | [INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT), |
310 | [INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE), | |
311 | [INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT), | |
29511061 DW |
312 | [INPUT_BUTTON_SIDE] = SDL_BUTTON(SDL_BUTTON_X1), |
313 | [INPUT_BUTTON_EXTRA] = SDL_BUTTON(SDL_BUTTON_X2) | |
47c03744 DA |
314 | }; |
315 | static uint32_t prev_state; | |
316 | ||
317 | if (prev_state != state) { | |
318 | qemu_input_update_buttons(scon->dcl.con, bmap, prev_state, state); | |
319 | prev_state = state; | |
320 | } | |
321 | ||
0337e412 | 322 | if (qemu_input_is_absolute(scon->dcl.con)) { |
d9f06262 JM |
323 | qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, |
324 | x, 0, surface_width(scon->surface)); | |
325 | qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, | |
326 | y, 0, surface_height(scon->surface)); | |
afbc0dd6 CR |
327 | } else { |
328 | if (guest_cursor) { | |
329 | x -= guest_x; | |
330 | y -= guest_y; | |
331 | guest_x += x; | |
332 | guest_y += y; | |
333 | dx = x; | |
334 | dy = y; | |
335 | } | |
336 | qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_X, dx); | |
337 | qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_Y, dy); | |
47c03744 DA |
338 | } |
339 | qemu_input_event_sync(); | |
340 | } | |
341 | ||
5d0fe650 | 342 | static void toggle_full_screen(struct sdl2_console *scon) |
47c03744 | 343 | { |
47c03744 DA |
344 | gui_fullscreen = !gui_fullscreen; |
345 | if (gui_fullscreen) { | |
46522a82 GH |
346 | SDL_SetWindowFullscreen(scon->real_window, |
347 | SDL_WINDOW_FULLSCREEN_DESKTOP); | |
47c03744 DA |
348 | gui_saved_grab = gui_grab; |
349 | sdl_grab_start(scon); | |
350 | } else { | |
47c03744 DA |
351 | if (!gui_saved_grab) { |
352 | sdl_grab_end(scon); | |
353 | } | |
46522a82 | 354 | SDL_SetWindowFullscreen(scon->real_window, 0); |
47c03744 | 355 | } |
0b71a5d5 | 356 | sdl2_redraw(scon); |
47c03744 DA |
357 | } |
358 | ||
849bbe60 | 359 | static int get_mod_state(void) |
47c03744 | 360 | { |
849bbe60 | 361 | SDL_Keymod mod = SDL_GetModState(); |
47c03744 DA |
362 | |
363 | if (alt_grab) { | |
849bbe60 | 364 | return (mod & (gui_grab_code | KMOD_LSHIFT)) == |
47c03744 DA |
365 | (gui_grab_code | KMOD_LSHIFT); |
366 | } else if (ctrl_grab) { | |
849bbe60 | 367 | return (mod & KMOD_RCTRL) == KMOD_RCTRL; |
47c03744 | 368 | } else { |
849bbe60 | 369 | return (mod & gui_grab_code) == gui_grab_code; |
47c03744 | 370 | } |
849bbe60 | 371 | } |
47c03744 | 372 | |
83047345 VR |
373 | static void *sdl2_win32_get_hwnd(struct sdl2_console *scon) |
374 | { | |
375 | #ifdef CONFIG_WIN32 | |
376 | SDL_SysWMinfo info; | |
377 | ||
378 | SDL_VERSION(&info.version); | |
379 | if (SDL_GetWindowWMInfo(scon->real_window, &info)) { | |
380 | return info.info.win.window; | |
381 | } | |
382 | #endif | |
383 | return NULL; | |
384 | } | |
385 | ||
849bbe60 JM |
386 | static void handle_keydown(SDL_Event *ev) |
387 | { | |
388 | int win; | |
389 | struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); | |
afb92eb9 | 390 | int gui_key_modifier_pressed = get_mod_state(); |
07333e1c | 391 | int gui_keysym = 0; |
849bbe60 | 392 | |
32ec9839 CD |
393 | if (!scon) { |
394 | return; | |
395 | } | |
396 | ||
849bbe60 | 397 | if (!scon->ignore_hotkeys && gui_key_modifier_pressed && !ev->key.repeat) { |
47c03744 | 398 | switch (ev->key.keysym.scancode) { |
363f59d9 GH |
399 | case SDL_SCANCODE_2: |
400 | case SDL_SCANCODE_3: | |
401 | case SDL_SCANCODE_4: | |
402 | case SDL_SCANCODE_5: | |
403 | case SDL_SCANCODE_6: | |
404 | case SDL_SCANCODE_7: | |
405 | case SDL_SCANCODE_8: | |
406 | case SDL_SCANCODE_9: | |
56f289f3 CR |
407 | if (gui_grab) { |
408 | sdl_grab_end(scon); | |
409 | } | |
410 | ||
363f59d9 GH |
411 | win = ev->key.keysym.scancode - SDL_SCANCODE_1; |
412 | if (win < sdl2_num_outputs) { | |
413 | sdl2_console[win].hidden = !sdl2_console[win].hidden; | |
414 | if (sdl2_console[win].real_window) { | |
415 | if (sdl2_console[win].hidden) { | |
416 | SDL_HideWindow(sdl2_console[win].real_window); | |
417 | } else { | |
418 | SDL_ShowWindow(sdl2_console[win].real_window); | |
419 | } | |
420 | } | |
421 | gui_keysym = 1; | |
422 | } | |
423 | break; | |
47c03744 DA |
424 | case SDL_SCANCODE_F: |
425 | toggle_full_screen(scon); | |
426 | gui_keysym = 1; | |
427 | break; | |
f8d2c936 GH |
428 | case SDL_SCANCODE_G: |
429 | gui_keysym = 1; | |
430 | if (!gui_grab) { | |
431 | sdl_grab_start(scon); | |
432 | } else if (!gui_fullscreen) { | |
433 | sdl_grab_end(scon); | |
434 | } | |
435 | break; | |
47c03744 | 436 | case SDL_SCANCODE_U: |
64bf97e5 | 437 | sdl2_window_resize(scon); |
0b71a5d5 GH |
438 | if (!scon->opengl) { |
439 | /* re-create scon->texture */ | |
440 | sdl2_2d_switch(&scon->dcl, scon->surface); | |
441 | } | |
47c03744 DA |
442 | gui_keysym = 1; |
443 | break; | |
46522a82 | 444 | #if 0 |
47c03744 DA |
445 | case SDL_SCANCODE_KP_PLUS: |
446 | case SDL_SCANCODE_KP_MINUS: | |
447 | if (!gui_fullscreen) { | |
448 | int scr_w, scr_h; | |
449 | int width, height; | |
450 | SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); | |
451 | ||
452 | width = MAX(scr_w + (ev->key.keysym.scancode == | |
453 | SDL_SCANCODE_KP_PLUS ? 50 : -50), | |
454 | 160); | |
455 | height = (surface_height(scon->surface) * width) / | |
456 | surface_width(scon->surface); | |
46522a82 GH |
457 | fprintf(stderr, "%s: scale to %dx%d\n", |
458 | __func__, width, height); | |
47c03744 | 459 | sdl_scale(scon, width, height); |
0b71a5d5 | 460 | sdl2_redraw(scon); |
47c03744 DA |
461 | gui_keysym = 1; |
462 | } | |
46522a82 | 463 | #endif |
47c03744 DA |
464 | default: |
465 | break; | |
466 | } | |
467 | } | |
468 | if (!gui_keysym) { | |
8fc1a3f5 | 469 | sdl2_process_key(scon, &ev->key); |
47c03744 DA |
470 | } |
471 | } | |
472 | ||
473 | static void handle_keyup(SDL_Event *ev) | |
474 | { | |
5d0fe650 | 475 | struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); |
47c03744 | 476 | |
32ec9839 CD |
477 | if (!scon) { |
478 | return; | |
479 | } | |
480 | ||
849bbe60 | 481 | scon->ignore_hotkeys = false; |
07333e1c | 482 | sdl2_process_key(scon, &ev->key); |
47c03744 DA |
483 | } |
484 | ||
f2335791 GH |
485 | static void handle_textinput(SDL_Event *ev) |
486 | { | |
48db08cf | 487 | struct sdl2_console *scon = get_scon_from_window(ev->text.windowID); |
f2335791 GH |
488 | QemuConsole *con = scon ? scon->dcl.con : NULL; |
489 | ||
32ec9839 CD |
490 | if (!con) { |
491 | return; | |
492 | } | |
493 | ||
9db018ac | 494 | if (QEMU_IS_TEXT_CONSOLE(con)) { |
cc6ba2c6 | 495 | qemu_text_console_put_string(QEMU_TEXT_CONSOLE(con), ev->text.text, strlen(ev->text.text)); |
f2335791 | 496 | } |
f2335791 GH |
497 | } |
498 | ||
47c03744 DA |
499 | static void handle_mousemotion(SDL_Event *ev) |
500 | { | |
501 | int max_x, max_y; | |
48db08cf | 502 | struct sdl2_console *scon = get_scon_from_window(ev->motion.windowID); |
47c03744 | 503 | |
49213b72 | 504 | if (!scon || !qemu_console_is_graphic(scon->dcl.con)) { |
28216716 JM |
505 | return; |
506 | } | |
507 | ||
0337e412 | 508 | if (qemu_input_is_absolute(scon->dcl.con) || absolute_enabled) { |
47c03744 DA |
509 | int scr_w, scr_h; |
510 | SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); | |
511 | max_x = scr_w - 1; | |
512 | max_y = scr_h - 1; | |
24952847 JM |
513 | if (gui_grab && !gui_fullscreen |
514 | && (ev->motion.x == 0 || ev->motion.y == 0 || | |
515 | ev->motion.x == max_x || ev->motion.y == max_y)) { | |
47c03744 DA |
516 | sdl_grab_end(scon); |
517 | } | |
518 | if (!gui_grab && | |
519 | (ev->motion.x > 0 && ev->motion.x < max_x && | |
520 | ev->motion.y > 0 && ev->motion.y < max_y)) { | |
521 | sdl_grab_start(scon); | |
522 | } | |
523 | } | |
0337e412 | 524 | if (gui_grab || qemu_input_is_absolute(scon->dcl.con) || absolute_enabled) { |
3f2fde2a | 525 | sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel, |
47c03744 DA |
526 | ev->motion.x, ev->motion.y, ev->motion.state); |
527 | } | |
528 | } | |
529 | ||
530 | static void handle_mousebutton(SDL_Event *ev) | |
531 | { | |
532 | int buttonstate = SDL_GetMouseState(NULL, NULL); | |
533 | SDL_MouseButtonEvent *bev; | |
48db08cf | 534 | struct sdl2_console *scon = get_scon_from_window(ev->button.windowID); |
47c03744 | 535 | |
49213b72 | 536 | if (!scon || !qemu_console_is_graphic(scon->dcl.con)) { |
28216716 JM |
537 | return; |
538 | } | |
539 | ||
47c03744 | 540 | bev = &ev->button; |
0337e412 | 541 | if (!gui_grab && !qemu_input_is_absolute(scon->dcl.con)) { |
47c03744 DA |
542 | if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) { |
543 | /* start grabbing all events */ | |
544 | sdl_grab_start(scon); | |
545 | } | |
546 | } else { | |
47c03744 DA |
547 | if (ev->type == SDL_MOUSEBUTTONDOWN) { |
548 | buttonstate |= SDL_BUTTON(bev->button); | |
549 | } else { | |
550 | buttonstate &= ~SDL_BUTTON(bev->button); | |
551 | } | |
3f2fde2a CR |
552 | sdl_send_mouse_event(scon, 0, 0, bev->x, bev->y, buttonstate); |
553 | } | |
554 | } | |
555 | ||
556 | static void handle_mousewheel(SDL_Event *ev) | |
557 | { | |
48db08cf | 558 | struct sdl2_console *scon = get_scon_from_window(ev->wheel.windowID); |
3f2fde2a CR |
559 | SDL_MouseWheelEvent *wev = &ev->wheel; |
560 | InputButton btn; | |
561 | ||
49213b72 | 562 | if (!scon || !qemu_console_is_graphic(scon->dcl.con)) { |
28216716 JM |
563 | return; |
564 | } | |
565 | ||
3f2fde2a | 566 | if (wev->y > 0) { |
f22d0af0 | 567 | btn = INPUT_BUTTON_WHEEL_UP; |
3f2fde2a | 568 | } else if (wev->y < 0) { |
f22d0af0 | 569 | btn = INPUT_BUTTON_WHEEL_DOWN; |
ed80f503 DP |
570 | } else if (wev->x < 0) { |
571 | btn = INPUT_BUTTON_WHEEL_RIGHT; | |
572 | } else if (wev->x > 0) { | |
573 | btn = INPUT_BUTTON_WHEEL_LEFT; | |
3f2fde2a CR |
574 | } else { |
575 | return; | |
47c03744 | 576 | } |
3f2fde2a CR |
577 | |
578 | qemu_input_queue_btn(scon->dcl.con, btn, true); | |
579 | qemu_input_event_sync(); | |
580 | qemu_input_queue_btn(scon->dcl.con, btn, false); | |
581 | qemu_input_event_sync(); | |
47c03744 DA |
582 | } |
583 | ||
1dfc5c88 | 584 | static void handle_windowevent(SDL_Event *ev) |
47c03744 | 585 | { |
1dfc5c88 | 586 | struct sdl2_console *scon = get_scon_from_window(ev->window.windowID); |
fe91f36a | 587 | bool allow_close = true; |
1dfc5c88 | 588 | |
08d49df0 AG |
589 | if (!scon) { |
590 | return; | |
591 | } | |
592 | ||
47c03744 DA |
593 | switch (ev->window.event) { |
594 | case SDL_WINDOWEVENT_RESIZED: | |
8b15d9f1 DA |
595 | { |
596 | QemuUIInfo info; | |
597 | memset(&info, 0, sizeof(info)); | |
598 | info.width = ev->window.data1; | |
599 | info.height = ev->window.data2; | |
ca19ef52 | 600 | dpy_set_ui_info(scon->dcl.con, &info, true); |
8b15d9f1 | 601 | } |
0b71a5d5 | 602 | sdl2_redraw(scon); |
47c03744 DA |
603 | break; |
604 | case SDL_WINDOWEVENT_EXPOSED: | |
0b71a5d5 | 605 | sdl2_redraw(scon); |
47c03744 DA |
606 | break; |
607 | case SDL_WINDOWEVENT_FOCUS_GAINED: | |
83047345 VR |
608 | win32_kbd_set_grab(gui_grab); |
609 | if (qemu_console_is_graphic(scon->dcl.con)) { | |
610 | win32_kbd_set_window(sdl2_win32_get_hwnd(scon)); | |
611 | } | |
612 | /* fall through */ | |
47c03744 | 613 | case SDL_WINDOWEVENT_ENTER: |
0337e412 | 614 | if (!gui_grab && (qemu_input_is_absolute(scon->dcl.con) || absolute_enabled)) { |
47c03744 DA |
615 | absolute_mouse_grab(scon); |
616 | } | |
849bbe60 JM |
617 | /* If a new console window opened using a hotkey receives the |
618 | * focus, SDL sends another KEYDOWN event to the new window, | |
619 | * closing the console window immediately after. | |
620 | * | |
621 | * Work around this by ignoring further hotkey events until a | |
622 | * key is released. | |
623 | */ | |
624 | scon->ignore_hotkeys = get_mod_state(); | |
47c03744 DA |
625 | break; |
626 | case SDL_WINDOWEVENT_FOCUS_LOST: | |
83047345 VR |
627 | if (qemu_console_is_graphic(scon->dcl.con)) { |
628 | win32_kbd_set_window(NULL); | |
629 | } | |
47c03744 DA |
630 | if (gui_grab && !gui_fullscreen) { |
631 | sdl_grab_end(scon); | |
632 | } | |
633 | break; | |
634 | case SDL_WINDOWEVENT_RESTORED: | |
63ed4907 | 635 | update_displaychangelistener(&scon->dcl, GUI_REFRESH_INTERVAL_DEFAULT); |
47c03744 DA |
636 | break; |
637 | case SDL_WINDOWEVENT_MINIMIZED: | |
63ed4907 | 638 | update_displaychangelistener(&scon->dcl, 500); |
47c03744 DA |
639 | break; |
640 | case SDL_WINDOWEVENT_CLOSE: | |
fc49e727 | 641 | if (qemu_console_is_graphic(scon->dcl.con)) { |
844fd50d | 642 | if (scon->opts->has_window_close && !scon->opts->window_close) { |
fe91f36a GH |
643 | allow_close = false; |
644 | } | |
645 | if (allow_close) { | |
e6dba048 | 646 | shutdown_action = SHUTDOWN_ACTION_POWEROFF; |
fc49e727 JM |
647 | qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI); |
648 | } | |
649 | } else { | |
650 | SDL_HideWindow(scon->real_window); | |
651 | scon->hidden = true; | |
47c03744 DA |
652 | } |
653 | break; | |
d3f3a0f4 | 654 | case SDL_WINDOWEVENT_SHOWN: |
bcf43cdc | 655 | scon->hidden = false; |
d3f3a0f4 HR |
656 | break; |
657 | case SDL_WINDOWEVENT_HIDDEN: | |
bcf43cdc | 658 | scon->hidden = true; |
d3f3a0f4 | 659 | break; |
47c03744 DA |
660 | } |
661 | } | |
662 | ||
63ed4907 | 663 | void sdl2_poll_events(struct sdl2_console *scon) |
47c03744 | 664 | { |
47c03744 | 665 | SDL_Event ev1, *ev = &ev1; |
fe91f36a | 666 | bool allow_close = true; |
56bdd4b6 | 667 | int idle = 1; |
47c03744 DA |
668 | |
669 | if (scon->last_vm_running != runstate_is_running()) { | |
670 | scon->last_vm_running = runstate_is_running(); | |
671 | sdl_update_caption(scon); | |
672 | } | |
673 | ||
47c03744 DA |
674 | while (SDL_PollEvent(ev)) { |
675 | switch (ev->type) { | |
676 | case SDL_KEYDOWN: | |
56bdd4b6 | 677 | idle = 0; |
47c03744 DA |
678 | handle_keydown(ev); |
679 | break; | |
680 | case SDL_KEYUP: | |
56bdd4b6 | 681 | idle = 0; |
47c03744 DA |
682 | handle_keyup(ev); |
683 | break; | |
f2335791 | 684 | case SDL_TEXTINPUT: |
56bdd4b6 | 685 | idle = 0; |
f2335791 GH |
686 | handle_textinput(ev); |
687 | break; | |
47c03744 | 688 | case SDL_QUIT: |
844fd50d | 689 | if (scon->opts->has_window_close && !scon->opts->window_close) { |
fe91f36a GH |
690 | allow_close = false; |
691 | } | |
692 | if (allow_close) { | |
e6dba048 | 693 | shutdown_action = SHUTDOWN_ACTION_POWEROFF; |
cf83f140 | 694 | qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI); |
47c03744 DA |
695 | } |
696 | break; | |
697 | case SDL_MOUSEMOTION: | |
56bdd4b6 | 698 | idle = 0; |
47c03744 DA |
699 | handle_mousemotion(ev); |
700 | break; | |
701 | case SDL_MOUSEBUTTONDOWN: | |
702 | case SDL_MOUSEBUTTONUP: | |
56bdd4b6 | 703 | idle = 0; |
47c03744 DA |
704 | handle_mousebutton(ev); |
705 | break; | |
3f2fde2a | 706 | case SDL_MOUSEWHEEL: |
56bdd4b6 | 707 | idle = 0; |
3f2fde2a CR |
708 | handle_mousewheel(ev); |
709 | break; | |
47c03744 | 710 | case SDL_WINDOWEVENT: |
1dfc5c88 | 711 | handle_windowevent(ev); |
47c03744 DA |
712 | break; |
713 | default: | |
714 | break; | |
715 | } | |
716 | } | |
56bdd4b6 JM |
717 | |
718 | if (idle) { | |
719 | if (scon->idle_counter < SDL2_MAX_IDLE_COUNT) { | |
720 | scon->idle_counter++; | |
721 | if (scon->idle_counter >= SDL2_MAX_IDLE_COUNT) { | |
722 | scon->dcl.update_interval = GUI_REFRESH_INTERVAL_DEFAULT; | |
723 | } | |
724 | } | |
725 | } else { | |
726 | scon->idle_counter = 0; | |
727 | scon->dcl.update_interval = SDL2_REFRESH_INTERVAL_BUSY; | |
728 | } | |
47c03744 DA |
729 | } |
730 | ||
731 | static void sdl_mouse_warp(DisplayChangeListener *dcl, | |
732 | int x, int y, int on) | |
733 | { | |
5d0fe650 | 734 | struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); |
28216716 JM |
735 | |
736 | if (!qemu_console_is_graphic(scon->dcl.con)) { | |
737 | return; | |
738 | } | |
739 | ||
47c03744 DA |
740 | if (on) { |
741 | if (!guest_cursor) { | |
86a088e6 | 742 | sdl_show_cursor(scon); |
47c03744 | 743 | } |
0337e412 | 744 | if (gui_grab || qemu_input_is_absolute(scon->dcl.con) || absolute_enabled) { |
47c03744 | 745 | SDL_SetCursor(guest_sprite); |
0337e412 | 746 | if (!qemu_input_is_absolute(scon->dcl.con) && !absolute_enabled) { |
47c03744 DA |
747 | SDL_WarpMouseInWindow(scon->real_window, x, y); |
748 | } | |
749 | } | |
750 | } else if (gui_grab) { | |
86a088e6 | 751 | sdl_hide_cursor(scon); |
47c03744 DA |
752 | } |
753 | guest_cursor = on; | |
754 | guest_x = x, guest_y = y; | |
755 | } | |
756 | ||
757 | static void sdl_mouse_define(DisplayChangeListener *dcl, | |
758 | QEMUCursor *c) | |
759 | { | |
760 | ||
761 | if (guest_sprite) { | |
762 | SDL_FreeCursor(guest_sprite); | |
763 | } | |
764 | ||
765 | if (guest_sprite_surface) { | |
766 | SDL_FreeSurface(guest_sprite_surface); | |
767 | } | |
768 | ||
769 | guest_sprite_surface = | |
770 | SDL_CreateRGBSurfaceFrom(c->data, c->width, c->height, 32, c->width * 4, | |
771 | 0xff0000, 0x00ff00, 0xff, 0xff000000); | |
772 | ||
773 | if (!guest_sprite_surface) { | |
774 | fprintf(stderr, "Failed to make rgb surface from %p\n", c); | |
775 | return; | |
776 | } | |
777 | guest_sprite = SDL_CreateColorCursor(guest_sprite_surface, | |
778 | c->hot_x, c->hot_y); | |
779 | if (!guest_sprite) { | |
780 | fprintf(stderr, "Failed to make color cursor from %p\n", c); | |
781 | return; | |
782 | } | |
783 | if (guest_cursor && | |
0337e412 | 784 | (gui_grab || qemu_input_is_absolute(dcl->con) || absolute_enabled)) { |
47c03744 DA |
785 | SDL_SetCursor(guest_sprite); |
786 | } | |
787 | } | |
788 | ||
789 | static void sdl_cleanup(void) | |
790 | { | |
791 | if (guest_sprite) { | |
792 | SDL_FreeCursor(guest_sprite); | |
793 | } | |
794 | SDL_QuitSubSystem(SDL_INIT_VIDEO); | |
795 | } | |
796 | ||
f1ddebd8 | 797 | static const DisplayChangeListenerOps dcl_2d_ops = { |
877417d9 GH |
798 | .dpy_name = "sdl2-2d", |
799 | .dpy_gfx_update = sdl2_2d_update, | |
800 | .dpy_gfx_switch = sdl2_2d_switch, | |
801 | .dpy_gfx_check_format = sdl2_2d_check_format, | |
802 | .dpy_refresh = sdl2_2d_refresh, | |
803 | .dpy_mouse_set = sdl_mouse_warp, | |
804 | .dpy_cursor_define = sdl_mouse_define, | |
47c03744 DA |
805 | }; |
806 | ||
0b71a5d5 GH |
807 | #ifdef CONFIG_OPENGL |
808 | static const DisplayChangeListenerOps dcl_gl_ops = { | |
809 | .dpy_name = "sdl2-gl", | |
810 | .dpy_gfx_update = sdl2_gl_update, | |
811 | .dpy_gfx_switch = sdl2_gl_switch, | |
812 | .dpy_gfx_check_format = console_gl_check_format, | |
813 | .dpy_refresh = sdl2_gl_refresh, | |
814 | .dpy_mouse_set = sdl_mouse_warp, | |
815 | .dpy_cursor_define = sdl_mouse_define, | |
cb47dc9a | 816 | |
db6cdfbe | 817 | .dpy_gl_scanout_disable = sdl2_gl_scanout_disable, |
f4c36bda | 818 | .dpy_gl_scanout_texture = sdl2_gl_scanout_texture, |
cb47dc9a | 819 | .dpy_gl_update = sdl2_gl_scanout_flush, |
0b71a5d5 | 820 | }; |
5e79d516 | 821 | |
a62c4a17 MAL |
822 | static bool |
823 | sdl2_gl_is_compatible_dcl(DisplayGLCtx *dgc, | |
824 | DisplayChangeListener *dcl) | |
825 | { | |
826 | return dcl->ops == &dcl_gl_ops; | |
827 | } | |
828 | ||
5e79d516 | 829 | static const DisplayGLCtxOps gl_ctx_ops = { |
a62c4a17 | 830 | .dpy_gl_ctx_is_compatible_dcl = sdl2_gl_is_compatible_dcl, |
5e79d516 MAL |
831 | .dpy_gl_ctx_create = sdl2_gl_create_context, |
832 | .dpy_gl_ctx_destroy = sdl2_gl_destroy_context, | |
833 | .dpy_gl_ctx_make_current = sdl2_gl_make_context_current, | |
834 | }; | |
0b71a5d5 GH |
835 | #endif |
836 | ||
5ee1718f | 837 | static void sdl2_display_early_init(DisplayOptions *o) |
0b71a5d5 | 838 | { |
fe91f36a GH |
839 | assert(o->type == DISPLAY_TYPE_SDL); |
840 | if (o->has_gl && o->gl) { | |
0b71a5d5 GH |
841 | #ifdef CONFIG_OPENGL |
842 | display_opengl = 1; | |
843 | #endif | |
0b71a5d5 GH |
844 | } |
845 | } | |
846 | ||
5ee1718f | 847 | static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) |
47c03744 | 848 | { |
47c03744 | 849 | uint8_t data = 0; |
47c03744 | 850 | int i; |
d8253671 | 851 | SDL_SysWMinfo info; |
a442fe2f | 852 | SDL_Surface *icon = NULL; |
77d910fb | 853 | char *dir; |
47c03744 | 854 | |
fe91f36a | 855 | assert(o->type == DISPLAY_TYPE_SDL); |
fe91f36a | 856 | |
82f483dd MAL |
857 | if (SDL_GetHintBoolean("QEMU_ENABLE_SDL_LOGGING", SDL_FALSE)) { |
858 | SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE); | |
859 | } | |
860 | ||
2313e482 | 861 | if (SDL_Init(SDL_INIT_VIDEO)) { |
47c03744 DA |
862 | fprintf(stderr, "Could not initialize SDL(%s) - exiting\n", |
863 | SDL_GetError()); | |
864 | exit(1); | |
865 | } | |
8c2b816f SK |
866 | #ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR /* only available since SDL 2.0.8 */ |
867 | SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); | |
868 | #endif | |
1dfea3f2 | 869 | #ifndef CONFIG_WIN32 |
d4761b65 | 870 | /* QEMU uses its own low level keyboard hook procedure on Windows */ |
44f017d0 | 871 | SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "1"); |
1dfea3f2 | 872 | #endif |
efc00a37 BB |
873 | #ifdef SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED |
874 | SDL_SetHint(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, "0"); | |
875 | #endif | |
083db9db | 876 | SDL_SetHint(SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4, "1"); |
d8253671 ST |
877 | memset(&info, 0, sizeof(info)); |
878 | SDL_VERSION(&info.version); | |
47c03744 | 879 | |
6fb34ffc TH |
880 | gui_fullscreen = o->has_full_screen && o->full_screen; |
881 | ||
9eafdeea TH |
882 | if (o->u.sdl.has_grab_mod) { |
883 | if (o->u.sdl.grab_mod == HOT_KEY_MOD_LSHIFT_LCTRL_LALT) { | |
884 | alt_grab = true; | |
885 | } else if (o->u.sdl.grab_mod == HOT_KEY_MOD_RCTRL) { | |
886 | ctrl_grab = true; | |
887 | } | |
888 | } | |
889 | ||
47c03744 DA |
890 | for (i = 0;; i++) { |
891 | QemuConsole *con = qemu_console_lookup_by_index(i); | |
f2335791 | 892 | if (!con) { |
47c03744 DA |
893 | break; |
894 | } | |
895 | } | |
896 | sdl2_num_outputs = i; | |
8efa5f29 GH |
897 | if (sdl2_num_outputs == 0) { |
898 | return; | |
899 | } | |
5d0fe650 | 900 | sdl2_console = g_new0(struct sdl2_console, sdl2_num_outputs); |
47c03744 DA |
901 | for (i = 0; i < sdl2_num_outputs; i++) { |
902 | QemuConsole *con = qemu_console_lookup_by_index(i); | |
85970a62 | 903 | assert(con != NULL); |
6624c38d GH |
904 | if (!qemu_console_is_graphic(con) && |
905 | qemu_console_get_index(con) != 0) { | |
f2335791 GH |
906 | sdl2_console[i].hidden = true; |
907 | } | |
0b71a5d5 | 908 | sdl2_console[i].idx = i; |
f88e5c57 | 909 | sdl2_console[i].opts = o; |
0b71a5d5 GH |
910 | #ifdef CONFIG_OPENGL |
911 | sdl2_console[i].opengl = display_opengl; | |
912 | sdl2_console[i].dcl.ops = display_opengl ? &dcl_gl_ops : &dcl_2d_ops; | |
5e79d516 | 913 | sdl2_console[i].dgc.ops = display_opengl ? &gl_ctx_ops : NULL; |
0b71a5d5 GH |
914 | #else |
915 | sdl2_console[i].opengl = 0; | |
f1ddebd8 | 916 | sdl2_console[i].dcl.ops = &dcl_2d_ops; |
0b71a5d5 | 917 | #endif |
47c03744 | 918 | sdl2_console[i].dcl.con = con; |
07333e1c | 919 | sdl2_console[i].kbd = qkbd_state_init(con); |
ac32b2ff | 920 | if (display_opengl) { |
5e79d516 | 921 | qemu_console_set_display_gl_ctx(con, &sdl2_console[i].dgc); |
ac32b2ff | 922 | } |
47c03744 | 923 | register_displaychangelistener(&sdl2_console[i].dcl); |
d8253671 | 924 | |
8417cd00 | 925 | #if defined(SDL_VIDEO_DRIVER_WINDOWS) || defined(SDL_VIDEO_DRIVER_X11) |
d8253671 | 926 | if (SDL_GetWindowWMInfo(sdl2_console[i].real_window, &info)) { |
8417cd00 GH |
927 | #if defined(SDL_VIDEO_DRIVER_WINDOWS) |
928 | qemu_console_set_window_id(con, (uintptr_t)info.info.win.window); | |
929 | #elif defined(SDL_VIDEO_DRIVER_X11) | |
d8253671 | 930 | qemu_console_set_window_id(con, info.info.x11.window); |
8417cd00 | 931 | #endif |
d8253671 | 932 | } |
8417cd00 | 933 | #endif |
47c03744 DA |
934 | } |
935 | ||
a442fe2f | 936 | #ifdef CONFIG_SDL_IMAGE |
77d910fb PB |
937 | dir = get_relocated_path(CONFIG_QEMU_ICONDIR "/hicolor/128x128/apps/qemu.png"); |
938 | icon = IMG_Load(dir); | |
a442fe2f | 939 | #else |
47c03744 | 940 | /* Load a 32x32x4 image. White pixels are transparent. */ |
77d910fb PB |
941 | dir = get_relocated_path(CONFIG_QEMU_ICONDIR "/hicolor/32x32/apps/qemu.bmp"); |
942 | icon = SDL_LoadBMP(dir); | |
a442fe2f DB |
943 | if (icon) { |
944 | uint32_t colorkey = SDL_MapRGB(icon->format, 255, 255, 255); | |
945 | SDL_SetColorKey(icon, SDL_TRUE, colorkey); | |
946 | } | |
947 | #endif | |
77d910fb | 948 | g_free(dir); |
a442fe2f DB |
949 | if (icon) { |
950 | SDL_SetWindowIcon(sdl2_console[0].real_window, icon); | |
47c03744 DA |
951 | } |
952 | ||
47c03744 DA |
953 | mouse_mode_notifier.notify = sdl_mouse_mode_change; |
954 | qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier); | |
955 | ||
47c03744 DA |
956 | sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0); |
957 | sdl_cursor_normal = SDL_GetCursor(); | |
958 | ||
7dafc679 VR |
959 | if (gui_fullscreen) { |
960 | sdl_grab_start(&sdl2_console[0]); | |
961 | } | |
962 | ||
47c03744 DA |
963 | atexit(sdl_cleanup); |
964 | } | |
5ee1718f GH |
965 | |
966 | static QemuDisplay qemu_display_sdl2 = { | |
967 | .type = DISPLAY_TYPE_SDL, | |
968 | .early_init = sdl2_display_early_init, | |
969 | .init = sdl2_display_init, | |
970 | }; | |
971 | ||
972 | static void register_sdl1(void) | |
973 | { | |
974 | qemu_display_register(&qemu_display_sdl2); | |
975 | } | |
976 | ||
977 | type_init(register_sdl1); | |
b36ae1c1 GH |
978 | |
979 | #ifdef CONFIG_OPENGL | |
980 | module_dep("ui-opengl"); | |
981 | #endif |